diff --git a/Makefile b/Makefile index 11c966202..79449870d 100644 --- a/Makefile +++ b/Makefile @@ -125,6 +125,8 @@ test.watch.wip: mix test.watch --only wip test.watch.wip2: mix test.watch --only wip2 +test.watch.bug: + mix test.watch --only bug test.db_reset: env MIX_ENV=test mix ecto.drop env MIX_ENV=test mix ecto.create diff --git a/Makefile.include.mk b/Makefile.include.mk index b100bb397..a44c3ad61 100644 --- a/Makefile.include.mk +++ b/Makefile.include.mk @@ -130,6 +130,10 @@ define test.help @echo " ....................................................." @echo " test.watch.wip : run @wip test in watch mode" @echo " ....................................................." + @echo " test.watch.wip2 : shortcut for lots of @wip around" + @echo " ....................................................." + @echo " test.watch.bug : sometimes fails for unkown reason" + @echo " ....................................................." @echo " test.db_reset : reset test database" @echo " | needed when add new migration" @echo " ....................................................." diff --git a/README.md b/README.md index c295a1c37..225d7e6d8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -![CPS Brand](https://github.com/mydearxym/mastani_server/blob/dev/docs/snapshots/cps_logo_md.png) +![CPS Brand](https://github.com/mydearxym/mastani_server/blob/dev/docs/snapshots/cps_logo_md.png?raw=true) ========= [![Build Status](https://travis-ci.org/coderplanets/coderplanets_server.svg?branch=dev)](https://travis-ci.org/coderplanets/coderplanets_server) [![codecov](https://codecov.io/gh/coderplanets/coderplanets_server/branch/dev/graph/badge.svg)](https://codecov.io/gh/coderplanets/coderplanets_server) diff --git a/config/config.exs b/config/config.exs index 1d0168e20..84065f80f 100644 --- a/config/config.exs +++ b/config/config.exs @@ -21,6 +21,7 @@ config :logger, :console, metadata: [:request_id] config :phoenix, :format_encoders, json: Jason +config :ecto, json_library: Jason # TODO move this config to secret later config :mastani_server, Helper.Guardian, @@ -50,6 +51,16 @@ config :mastani_server, :general, user_achieve_favorite_weight: 2, user_achieve_follow_weight: 3 +config :mastani_server, :customization, + theme: "cyan", + community_chart: false, + brainwash_free: false, + banner_layout: "digest", + contents_layout: "digest", + content_divider: false, + mark_viewed: true, + display_density: "20" + config :mastani_server, MastaniServerWeb.Gettext, default_locale: "zh_CN", locales: ~w(en zh_CN) import_config "#{Mix.env()}.exs" diff --git a/deploy/production/api_server.tar.gz b/deploy/production/api_server.tar.gz index c424b7f50..782762c7e 100644 Binary files a/deploy/production/api_server.tar.gz and b/deploy/production/api_server.tar.gz differ diff --git a/lib/helper/PublicIpPlug.ex b/lib/helper/PublicIpPlug.ex new file mode 100644 index 000000000..4d7824ffd --- /dev/null +++ b/lib/helper/PublicIpPlug.ex @@ -0,0 +1,85 @@ +# https://www.cogini.com/blog/getting-the-client-public-ip-address-in-phoenix/ +defmodule Helper.PublicIpPlug do + @moduledoc "Get public IP address of request from x-forwarded-for header" + @behaviour Plug + @app :mastani_server + + def init(opts), do: opts + + def call(%{assigns: %{ip: _}} = conn, _opts), do: conn + + def call(conn, _opts) do + process(conn, Plug.Conn.get_req_header(conn, "x-forwarded-for")) + end + + def process(conn, []) do + Plug.Conn.assign(conn, :ip, to_string(:inet.ntoa(get_peer_ip(conn)))) + end + + def process(conn, vals) do + if Application.get_env(@app, :trust_x_forwarded_for, false) do + ip_address = get_ip_address(conn, vals) + + # Rewrite standard remote_ip field with value from header + # See https://hexdocs.pm/plug/Plug.Conn.html + conn = %{conn | remote_ip: ip_address} + + Plug.Conn.assign(conn, :ip, to_string(:inet.ntoa(ip_address))) + else + Plug.Conn.assign(conn, :ip, to_string(:inet.ntoa(get_peer_ip(conn)))) + end + end + + defp get_ip_address(conn, vals) + defp get_ip_address(conn, []), do: get_peer_ip(conn) + + defp get_ip_address(conn, [val | _]) do + # Split into multiple values + comps = + val + |> String.split(~r{\s*,\s*}, trim: true) + # Get rid of "unknown" values + |> Enum.filter(&(&1 != "unknown")) + # Split IP from port, if any + |> Enum.map(&hd(String.split(&1, ":"))) + # Filter out blanks + |> Enum.filter(&(&1 != "")) + # Parse address into :inet.ip_address tuple + |> Enum.map(&parse_address(&1)) + # Elminate internal IP addreses, e.g. 192.168.1.1 + |> Enum.filter(&is_public_ip(&1)) + + case comps do + [] -> get_peer_ip(conn) + [comp | _] -> comp + end + end + + @spec get_peer_ip(Plug.Conn.t()) :: :inet.ip_address() + defp get_peer_ip(conn) do + {ip, _port} = conn.peer + ip + end + + @spec parse_address(String.t()) :: :inet.ip_address() + defp parse_address(ip) do + case :inet.parse_ipv4strict_address(to_charlist(ip)) do + {:ok, ip_address} -> ip_address + {:error, :einval} -> :einval + end + end + + # Whether the input is a valid, public IP address + # http://en.wikipedia.org/wiki/Private_network + @spec is_public_ip(:inet.ip_address() | atom) :: boolean + defp is_public_ip(ip_address) do + case ip_address do + {10, _, _, _} -> false + {192, 168, _, _} -> false + {172, second, _, _} when second >= 16 and second <= 31 -> false + {127, 0, 0, _} -> false + {_, _, _, _} -> true + :einval -> false + end + end +end diff --git a/lib/helper/query_builder.ex b/lib/helper/query_builder.ex index 1ffffda47..7eeea0a16 100644 --- a/lib/helper/query_builder.ex +++ b/lib/helper/query_builder.ex @@ -27,6 +27,24 @@ defmodule Helper.QueryBuilder do |> select([f], count(f.id)) end + def members_pack(queryable, %{count: _, type: :job}) do + queryable + |> group_by([f], f.job_id) + |> select([f], count(f.id)) + end + + def members_pack(queryable, %{count: _, type: :video}) do + queryable + |> group_by([f], f.video_id) + |> select([f], count(f.id)) + end + + def members_pack(queryable, %{count: _, type: :repo}) do + queryable + |> group_by([f], f.repo_id) + |> select([f], count(f.id)) + end + def members_pack(queryable, %{count: _, type: :community}) do queryable |> group_by([f], f.community_id) @@ -167,6 +185,13 @@ defmodule Helper.QueryBuilder do where: t.title == ^tag_name ) + {:topic, topic}, queryable -> + from( + q in queryable, + join: t in assoc(q, :topics), + where: t.raw == ^topic + ) + {:category, catetory_raw}, queryable -> from( q in queryable, @@ -181,6 +206,13 @@ defmodule Helper.QueryBuilder do where: t.raw == ^community_raw ) + {:one_community, community_raw}, queryable -> + from( + q in queryable, + join: t in assoc(q, :community), + where: t.raw == ^community_raw + ) + {:first, first}, queryable -> queryable |> limit(^first) diff --git a/lib/helper/radar_search.ex b/lib/helper/radar_search.ex new file mode 100644 index 000000000..c5df77395 --- /dev/null +++ b/lib/helper/radar_search.ex @@ -0,0 +1,59 @@ +defmodule Helper.RadarSearch do + @moduledoc """ + find city info by ip + refer: https://lbs.amap.com/api/webservice/guide/api/ipconfig/?sug_index=0 + """ + use Tesla, only: [:get] + import Helper.Utils, only: [get_config: 2] + + @endpoint "https://restapi.amap.com/v3/ip" + @ip_service_key get_config(:radar_search, :ip_service) + @timeout_limit 5000 + + # plug(Tesla.Middleware.BaseUrl, "https://restapi.amap.com/v3/ip") + plug(Tesla.Middleware.Retry, delay: 200, max_retries: 2) + plug(Tesla.Middleware.Timeout, timeout: @timeout_limit) + plug(Tesla.Middleware.JSON) + + @doc """ + this is only match fail situation in test + """ + def locate_city(ip \\ "14.196.0.0") + + def locate_city(:fake_ip) do + {:error, "not found"} + end + + # http://ip.yqie.com/search.aspx?searchword=%E6%88%90%E9%83%BD%E5%B8%82 + def locate_city(ip) do + query = [ip: ip, key: @ip_service_key] + + with true <- Mix.env() !== :test do + case get(@endpoint, query: query) do + %{status: 200, body: body} -> + handle_result({:ok, body["city"]}) + + _ -> + {:error, "error"} + end + else + _ -> + {:ok, "成都"} + # {:error, "error"} + end + end + + defp handle_result({:ok, result}) do + case result do + [] -> {:error, "not found"} + _ -> cut_tail({:ok, result}) + end + end + + defp cut_tail({:ok, result}) do + case String.last(result) == "市" do + true -> {:ok, String.trim_trailing(result, "市")} + false -> {:ok, result} + end + end +end diff --git a/lib/helper/utils.ex b/lib/helper/utils.ex index f1fd15466..d55682018 100644 --- a/lib/helper/utils.ex +++ b/lib/helper/utils.ex @@ -6,10 +6,20 @@ defmodule Helper.Utils do import Helper.ErrorHandler import Helper.ErrorCode - def get_config(section, key, app \\ :mastani_server) do + def get_config(section, key, app \\ :mastani_server) + + def get_config(section, :all, app) do + app + |> Application.get_env(section) + |> case do + nil -> "" + config -> config + end + end + + def get_config(section, key, app) do app |> Application.get_env(section) - # |> IO.inspect(label: "debug ci") |> case do nil -> "" config -> Keyword.get(config, key) @@ -23,6 +33,7 @@ defmodule Helper.Utils do def done(_, :boolean), do: {:ok, true} def done(nil, err_msg), do: {:error, err_msg} def done({:ok, _}, with: result), do: {:ok, result} + def done({:error, error}, with: _result), do: {:error, error} def done({:ok, %{id: id}}, :status), do: {:ok, %{done: true, id: id}} def done({:error, _}, :status), do: {:ok, %{done: false}} @@ -69,6 +80,39 @@ defmodule Helper.Utils do map |> Enum.reduce(%{}, fn {key, val}, acc -> Map.put(acc, to_string(key), val) end) end + @doc """ + Recursivly camelize the map keys + usage: convert factory attrs to used for simu Graphql parmas + """ + def camelize_map_key(map) do + map_list = + Enum.map(map, fn {k, v} -> + v = + cond do + is_datetime?(v) -> + DateTime.to_iso8601(v) + + is_map(v) -> + camelize_map_key(safe_map(v)) + + true -> + v + end + + map_to_camel({k, v}) + end) + + Enum.into(map_list, %{}) + end + + defp safe_map(%{__struct__: _} = map), do: Map.from_struct(map) + defp safe_map(map), do: map + + defp map_to_camel({k, v}), do: {Recase.to_camel(to_string(k)), v} + + def is_datetime?(%DateTime{}), do: true + def is_datetime?(_), do: false + def deep_merge(left, right) do Map.merge(left, right, &deep_resolve/3) end @@ -87,10 +131,15 @@ defmodule Helper.Utils do def map_atom_value(attrs, :string) do results = Enum.map(attrs, fn {k, v} -> - if is_atom(v) do - {k, to_string(v)} - else - {k, v} + cond do + v == true or v == false -> + {k, v} + + is_atom(v) -> + {k, v |> to_string() |> String.downcase()} + + true -> + {k, v} end end) @@ -106,4 +155,15 @@ defmodule Helper.Utils do # NOT a map. We fall back to standard merge behavior, preferring # the value on the right. defp deep_resolve(_key, _left, right), do: right + + @doc """ + ["a", "b", "c", "c"] => %{"a" => 1, "b" => 1, "c" => 2} + """ + def count_words(words) when is_list(words) do + Enum.reduce(words, %{}, &update_word_count/2) + end + + defp update_word_count(word, acc) do + Map.update(acc, to_string(word), 1, &(&1 + 1)) + end end diff --git a/lib/mastani_server/accounts/accounts.ex b/lib/mastani_server/accounts/accounts.ex index 0977481cb..fc5b2cfa0 100644 --- a/lib/mastani_server/accounts/accounts.ex +++ b/lib/mastani_server/accounts/accounts.ex @@ -7,6 +7,7 @@ defmodule MastaniServer.Accounts do Customization, Fans, FavoriteCategory, + Publish, Mails, Profile, ReactedContents @@ -15,6 +16,7 @@ defmodule MastaniServer.Accounts do # profile defdelegate update_profile(user, attrs), to: Profile defdelegate github_signin(github_user), to: Profile + defdelegate github_signin(github_user, remote_ip), to: Profile defdelegate default_subscribed_communities(filter), to: Profile defdelegate subscribed_communities(user, filter), to: Profile @@ -28,6 +30,13 @@ defmodule MastaniServer.Accounts do # achievement defdelegate achieve(user, operation, key), to: Achievements + defdelegate list_editable_communities(user, filter), to: Achievements + defdelegate downgrade_achievement(user, action, count), to: Achievements + # defdelegate list_editable_communities(filter), to: Achievements + + # publish + defdelegate published_contents(user, thread, filter), to: Publish + defdelegate published_comments(user, thread, filter), to: Publish # fans defdelegate follow(user, follower), to: Fans @@ -37,6 +46,7 @@ defmodule MastaniServer.Accounts do # reacted contents defdelegate reacted_contents(thread, react, filter, user), to: ReactedContents + defdelegate reacted_contents(thread, react, category_id, filter, user), to: ReactedContents # mentions defdelegate fetch_mentions(user, filter), to: Mails @@ -56,6 +66,7 @@ defmodule MastaniServer.Accounts do defdelegate has_purchased?(user, key), to: Billing # customization - defdelegate add_custom_setting(user, key, value), to: Customization - defdelegate add_custom_setting(user, key), to: Customization + defdelegate get_customization(user), to: Customization + defdelegate set_customization(user, key, value), to: Customization + defdelegate set_customization(user, options), to: Customization end diff --git a/lib/mastani_server/accounts/achievement.ex b/lib/mastani_server/accounts/achievement.ex index 6faf1b0b7..4ca6d67e3 100644 --- a/lib/mastani_server/accounts/achievement.ex +++ b/lib/mastani_server/accounts/achievement.ex @@ -4,7 +4,7 @@ defmodule MastaniServer.Accounts.Achievement do use Ecto.Schema import Ecto.Changeset - alias MastaniServer.Accounts.User + alias MastaniServer.Accounts.{User, SourceContribute} @required_fields ~w(user_id)a @optional_fields ~w(contents_stared_count contents_favorited_count contents_watched_count followers_count reputation)a @@ -18,6 +18,8 @@ defmodule MastaniServer.Accounts.Achievement do field(:contents_watched_count, :integer, default: 0) field(:followers_count, :integer, default: 0) field(:reputation, :integer, default: 0) + # source_contribute + embeds_one(:source_contribute, SourceContribute, on_replace: :delete) timestamps(type: :utc_datetime) end diff --git a/lib/mastani_server/accounts/customization.ex b/lib/mastani_server/accounts/customization.ex index 828e0400f..f0afe6673 100644 --- a/lib/mastani_server/accounts/customization.ex +++ b/lib/mastani_server/accounts/customization.ex @@ -8,7 +8,7 @@ defmodule MastaniServer.Accounts.Customization do alias MastaniServer.Accounts.User @required_fields ~w(user_id)a - @optional_fields ~w(theme sidebar_layout community_chart brainwash_free)a + @optional_fields ~w(theme sidebar_layout community_chart brainwash_free banner_layout contents_layout content_divider mark_viewed display_density)a @type t :: %Customization{} schema "customizations" do @@ -19,6 +19,13 @@ defmodule MastaniServer.Accounts.Customization do field(:community_chart, :boolean) field(:brainwash_free, :boolean) + field(:banner_layout, :string) + field(:contents_layout, :string) + field(:content_divider, :boolean) + field(:mark_viewed, :boolean) + # TODO: change to number + field(:display_density, :string) + timestamps(type: :utc_datetime) end diff --git a/lib/mastani_server/accounts/delegates/achievements.ex b/lib/mastani_server/accounts/delegates/achievements.ex index 4f2c483c2..6bf21fad1 100644 --- a/lib/mastani_server/accounts/delegates/achievements.ex +++ b/lib/mastani_server/accounts/delegates/achievements.ex @@ -7,7 +7,8 @@ defmodule MastaniServer.Accounts.Delegate.Achievements do 3. create content been favorited by other user + 2 4. followed by other user + 3 """ - import Helper.Utils, only: [get_config: 2] + import Ecto.Query, warn: false + import Helper.Utils, only: [get_config: 2, done: 1] import ShortMaps alias Helper.{ORM, SpecType} @@ -24,7 +25,7 @@ defmodule MastaniServer.Accounts.Delegate.Achievements do @spec achieve(User.t(), atom, atom) :: SpecType.done() def achieve(%User{id: user_id}, :add, :follow) do with {:ok, achievement} <- ORM.findby_or_insert(Achievement, ~m(user_id)a, ~m(user_id)a) do - followers_count = achievement.followers_count + @follow_weight + followers_count = achievement.followers_count + 1 reputation = achievement.reputation + @follow_weight achievement @@ -37,8 +38,8 @@ defmodule MastaniServer.Accounts.Delegate.Achievements do """ def achieve(%User{id: user_id}, :minus, :follow) do with {:ok, achievement} <- ORM.findby_or_insert(Achievement, ~m(user_id)a, ~m(user_id)a) do - followers_count = achievement.followers_count |> safe_minus(@follow_weight) - reputation = achievement.reputation |> safe_minus(@follow_weight) + followers_count = max(achievement.followers_count - 1, 0) + reputation = max(achievement.reputation - @follow_weight, 0) achievement |> ORM.update(~m(followers_count reputation)a) @@ -50,7 +51,7 @@ defmodule MastaniServer.Accounts.Delegate.Achievements do """ def achieve(%User{id: user_id} = _user, :add, :star) 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 + @star_weight + contents_stared_count = achievement.contents_stared_count + 1 reputation = achievement.reputation + @star_weight achievement @@ -63,8 +64,8 @@ defmodule MastaniServer.Accounts.Delegate.Achievements do """ def achieve(%User{id: user_id} = _user, :minus, :star) 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 |> safe_minus(@star_weight) - reputation = achievement.reputation |> safe_minus(@star_weight) + contents_stared_count = max(achievement.contents_stared_count - 1, 0) + reputation = max(achievement.reputation - @star_weight, 0) achievement |> ORM.update(~m(contents_stared_count reputation)a) @@ -76,7 +77,7 @@ defmodule MastaniServer.Accounts.Delegate.Achievements do """ def achieve(%User{id: user_id} = _user, :add, :favorite) 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 + @favorite_weight + contents_favorited_count = achievement.contents_favorited_count + 1 reputation = achievement.reputation + @favorite_weight achievement @@ -89,10 +90,9 @@ defmodule MastaniServer.Accounts.Delegate.Achievements do """ def achieve(%User{id: user_id} = _user, :minus, :favorite) 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 |> safe_minus(@favorite_weight) + contents_favorited_count = max(achievement.contents_favorited_count - 1, 0) - reputation = achievement.reputation |> safe_minus(@favorite_weight) + reputation = max(achievement.reputation - @favorite_weight, 0) achievement |> ORM.update(~m(contents_favorited_count reputation)a) @@ -111,6 +111,19 @@ defmodule MastaniServer.Accounts.Delegate.Achievements do # IO.inspect("acheiveements plus") # end + @doc """ + only used for user delete the farorited category, other case is auto + """ + def downgrade_achievement(%User{id: user_id}, :favorite, 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) + + achievement + |> ORM.update(~m(contents_favorited_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 @@ -121,4 +134,20 @@ defmodule MastaniServer.Accounts.Delegate.Achievements do count - unit end end + + @doc """ + list communities which the user is editor in it + """ + alias MastaniServer.CMS.CommunityEditor + + def list_editable_communities(%User{id: user_id}, %{page: page, size: size}) do + with {:ok, user} <- ORM.find(User, user_id) do + CommunityEditor + |> where([e], e.user_id == ^user.id) + |> join(:inner, [e], c in assoc(e, :community)) + |> select([e, c], c) + |> ORM.paginater(page: page, size: size) + |> done() + end + end end diff --git a/lib/mastani_server/accounts/delegates/customization.ex b/lib/mastani_server/accounts/delegates/customization.ex index 799b627be..e5a9f6608 100644 --- a/lib/mastani_server/accounts/delegates/customization.ex +++ b/lib/mastani_server/accounts/delegates/customization.ex @@ -1,23 +1,46 @@ defmodule MastaniServer.Accounts.Delegate.Customization do + @moduledoc """ + customization for user + """ import Ecto.Query, warn: false + import Helper.Utils, only: [get_config: 2, map_atom_value: 2] + alias Helper.ORM alias MastaniServer.Accounts alias MastaniServer.Accounts.{User, Customization} - alias Helper.ORM - # ... - # TODO: Constants + + @default_customization get_config(:customization, :all) |> Enum.into(%{}) + + @doc """ + get user's customization, if not have, return default customization + """ + def get_customization(%User{id: user_id}) do + case ORM.find_by(Customization, user_id: user_id) do + {:ok, customization} -> + customization = customization |> Map.from_struct() |> filter_nil_value + {:ok, Map.merge(@default_customization, customization)} + + {:error, _} -> + {:ok, @default_customization} + end + end @doc """ add custom setting to user """ # for map_size # see https://stackoverflow.com/questions/33248816/pattern-match-function-against-empty-map - def add_custom_setting(%User{} = _user, map) when map_size(map) == 0 do + def set_customization(%User{} = _user, map) when map_size(map) == 0 do {:error, "AccountCustomization: invalid option or not purchased"} end - def add_custom_setting(%User{} = user, map) when is_map(map) do - valid? = map |> Map.keys() |> Enum.all?(&can_set?(user, &1, :boolean)) + def set_customization(%User{} = user, map) when is_map(map) do + map = map |> map_atom_value(:string) + + valid? = + map + |> Map.keys() + |> Enum.all?(&can_set?(user, &1, :boolean)) case valid? do true -> @@ -29,7 +52,7 @@ defmodule MastaniServer.Accounts.Delegate.Customization do end end - def add_custom_setting(%User{} = user, key, value \\ true) do + def set_customization(%User{} = user, key, value \\ true) do with {:ok, key} <- can_set?(user, key) do attrs = Map.put(%{user_id: user.id}, key, value) Customization |> ORM.upsert_by([user_id: user.id], attrs) @@ -61,7 +84,14 @@ defmodule MastaniServer.Accounts.Delegate.Customization do # sidebar_layout -- user can arrange subscribed community index """ def valid_custom_items(:free) do - [:theme, :sidebar_layout] + [ + :sidebar_layout, + :banner_layout, + :contents_layout, + :content_divider, + :mark_viewed, + :display_density + ] end @doc """ @@ -71,6 +101,10 @@ defmodule MastaniServer.Accounts.Delegate.Customization do def valid_custom_items(:advance) do # NOTE: :brainwash_free aka. "ads_free" # use brainwash to avoid brower-block-plugins - [:brainwash_free, :community_chart] + [:theme, :brainwash_free, :community_chart] + end + + defp filter_nil_value(map) do + for {k, v} <- map, !is_nil(v), into: %{}, do: {k, v} end end diff --git a/lib/mastani_server/accounts/delegates/fans.ex b/lib/mastani_server/accounts/delegates/fans.ex index 866017574..3e5e6c60e 100644 --- a/lib/mastani_server/accounts/delegates/fans.ex +++ b/lib/mastani_server/accounts/delegates/fans.ex @@ -24,7 +24,8 @@ defmodule MastaniServer.Accounts.Delegate.Fans do Multi.new() |> Multi.insert( :create_follower, - UserFollower.changeset(%UserFollower{}, ~m(user_id follower_id)a) + # UserFollower.changeset(%UserFollower{}, ~m(user_id follower_id)a) + UserFollower.changeset(%UserFollower{}, %{user_id: follower_id, follower_id: user_id}) ) |> Multi.insert( :create_following, @@ -46,7 +47,7 @@ defmodule MastaniServer.Accounts.Delegate.Fans do @spec follow_result({:ok, map()}) :: SpecType.done() defp follow_result({:ok, %{create_follower: user_follower}}) do - User |> ORM.find(user_follower.follower_id) + User |> ORM.find(user_follower.user_id) end defp follow_result({:error, :create_follower, _result, _steps}) do @@ -70,7 +71,7 @@ defmodule MastaniServer.Accounts.Delegate.Fans do {:ok, _follow_user} <- ORM.find(User, follower_id) do Multi.new() |> Multi.run(:delete_follower, fn _ -> - ORM.findby_delete(UserFollower, ~m(user_id follower_id)a) + ORM.findby_delete(UserFollower, %{user_id: follower_id, follower_id: user_id}) end) |> Multi.run(:delete_following, fn _ -> ORM.findby_delete(UserFollowing, %{user_id: user_id, following_id: follower_id}) @@ -111,8 +112,8 @@ defmodule MastaniServer.Accounts.Delegate.Fans do @spec fetch_followers(User.t(), map()) :: {:ok, map()} | {:error, String.t()} def fetch_followers(%User{id: user_id}, filter) do UserFollower - |> where([uf], uf.follower_id == ^user_id) - |> join(:inner, [uf], u in assoc(uf, :user)) + |> where([uf], uf.user_id == ^user_id) + |> join(:inner, [uf], u in assoc(uf, :follower)) |> load_fans(filter) end diff --git a/lib/mastani_server/accounts/delegates/favorite_category.ex b/lib/mastani_server/accounts/delegates/favorite_category.ex index e0716f965..7bc4875f7 100644 --- a/lib/mastani_server/accounts/delegates/favorite_category.ex +++ b/lib/mastani_server/accounts/delegates/favorite_category.ex @@ -4,20 +4,26 @@ defmodule MastaniServer.Accounts.Delegate.FavoriteCategory do """ import Ecto.Query, warn: false + alias Helper.QueryBuilder + import Helper.ErrorCode - import Helper.Utils, only: [done: 1] + import Helper.Utils, only: [done: 1, count_words: 1] import ShortMaps alias Helper.ORM + alias MastaniServer.Accounts alias MastaniServer.Accounts.{FavoriteCategory, User} alias MastaniServer.{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 - FavoriteCategory |> ORM.create(attrs |> Map.merge(~m(user_id)a)) + 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)]} @@ -26,34 +32,75 @@ defmodule MastaniServer.Accounts.Delegate.FavoriteCategory do 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 - category |> ORM.update(attrs) + 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 & videos) 's author, + # and downgrade their's acieveents + # NOTE: this is too fucking violent and should be refactor later + # we find favroted posts/jobs/videos 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, video_author_ids} = affected_author_ids(:video, CMS.VideoFavorite, 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 ++ video_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) - |> Multi.run(:delete_favorite_record, fn _ -> - query = - from( - pf in CMS.PostFavorite, - where: pf.user_id == ^user_id, - where: pf.category_id == ^category.id - ) - - query |> Repo.delete_all() |> done() - end) |> Repo.transaction() |> delete_favorites_result() end end - defp delete_favorites_result({:ok, %{delete_favorite_record: result}}), do: {:ok, result} + # NOTE: this is too fucking violent and should be refactor later + # we find favroted posts/jobs/videos 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({:error, :delete_category, result, _steps}) do + defp delete_favorites_result({:ok, %{downgrade_achievement: result}}), do: {:ok, result} + + defp delete_favorites_result({:error, :delete_category, _result, _steps}) do {:error, [message: "delete category fails", code: ecode(:delete_fails)]} end @@ -79,58 +126,68 @@ defmodule MastaniServer.Accounts.Delegate.FavoriteCategory do end query + |> QueryBuilder.filter_pack(filter) |> ORM.paginater(page: page, size: size) |> done() end - # def update_favorie_category(), do ... - - # def delete_favorie_category(), do ... - - alias CMS.PostFavorite - - defp check_dup_category(content, category) do - case content.category_id !== category.id do - true -> {:ok, ""} - false -> {:error, [message: "viewer has already categoried", code: ecode(:already_did)]} - end - end - - defp content_favorite_result(:post, content_id, user_id) do - PostFavorite |> ORM.find_by(%{post_id: content_id, user_id: user_id}) - end - + @doc """ + set category for favorited content (post, job, video ...) + """ 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 _ -> - case content_favorite_result(thread, content_id, user.id) do - {:ok, content_favorite} -> check_dup_category(content_favorite, favorite_category) - {:error, _} -> CMS.reaction(thread, :favorite, content_id, user) + 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, error} -> {:error, error} + end end end) - |> Multi.run(:update_category_id, fn _ -> - {:ok, content_favorite} = content_favorite_result(thread, content_id, user.id) - + |> 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, ""} + error -> {:error, error} + 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(:inc_count, fn _ -> - favorite_category |> ORM.update(%{total_count: favorite_category.total_count + 1}) + |> 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, %{inc_count: result}}), do: {:ok, result} + 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, :update_category_id, _result, _steps}) do + 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 @@ -142,13 +199,17 @@ defmodule MastaniServer.Accounts.Delegate.FavoriteCategory do with {:ok, favorite_category} <- FavoriteCategory |> ORM.find_by(%{user_id: user.id, id: category_id}) do Multi.new() - |> Multi.run(:remove_favorite_record, fn _ -> - {:ok, content_favorite} = content_favorite_result(thread, content_id, user.id) - - content_favorite |> ORM.delete() + |> Multi.run(:undo_favorite_action, fn _ -> + CMS.undo_reaction(thread, :favorite, content_id, user) end) - |> Multi.run(:dec_count, fn _ -> - favorite_category |> ORM.update(%{total_count: max(favorite_category.total_count - 1, 0)}) + |> 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() @@ -156,9 +217,9 @@ defmodule MastaniServer.Accounts.Delegate.FavoriteCategory do end # @spec unset_favorites_result({:ok, map()}) :: {:ok, FavoriteCategory.t() } - defp unset_favorites_result({:ok, %{dec_count: result}}), do: {:ok, result} + defp unset_favorites_result({:ok, %{update_category_info: result}}), do: {:ok, result} - defp unset_favorites_result({:error, :remove_favorite_record, result, _steps}) do + defp unset_favorites_result({:error, :undo_favorite_action, result, _steps}) do # {:error, [message: "favorite content fails", code: ecode(:react_fails)]} {:error, result} end @@ -166,4 +227,23 @@ defmodule MastaniServer.Accounts.Delegate.FavoriteCategory do 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(:video, content_id, user_id), + do: VideoFavorite |> ORM.find_by(%{video_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/mastani_server/accounts/delegates/profile.ex b/lib/mastani_server/accounts/delegates/profile.ex index d4b5c80d7..b4a5a725c 100644 --- a/lib/mastani_server/accounts/delegates/profile.ex +++ b/lib/mastani_server/accounts/delegates/profile.ex @@ -6,8 +6,8 @@ defmodule MastaniServer.Accounts.Delegate.Profile do import Helper.Utils, only: [done: 1, get_config: 2] import ShortMaps - alias Helper.{Guardian, ORM, QueryBuilder} - alias MastaniServer.Accounts.{GithubUser, User} + alias Helper.{RadarSearch, Guardian, ORM, QueryBuilder} + alias MastaniServer.Accounts.{Achievement, GithubUser, User} alias MastaniServer.{CMS, Repo} alias Ecto.Multi @@ -52,16 +52,14 @@ defmodule MastaniServer.Accounts.Delegate.Profile do step 2.2: if access_token's github_id not exsit, then signup step 3: return mastani token """ - def github_signin(github_user) do + def github_signin(github_user, remote_ip \\ "127.0.0.1") do case ORM.find_by(GithubUser, github_id: to_string(github_user["id"])) do {:ok, g_user} -> {:ok, user} = ORM.find(User, g_user.user_id) - # IO.inspect label: "send back from db" token_info(user) {:error, _} -> - # IO.inspect label: "register then send" - register_github_user(github_user) + register_github_user(github_user, remote_ip) end end @@ -77,6 +75,8 @@ defmodule MastaniServer.Accounts.Delegate.Profile do get users subscribed communities """ def subscribed_communities(%User{id: id}, %{page: page, size: size} = filter) do + filter = filter |> Map.delete(:first) + CMS.CommunitySubscriber |> where([c], c.user_id == ^id) |> join(:inner, [c], cc in assoc(c, :community)) @@ -86,7 +86,7 @@ defmodule MastaniServer.Accounts.Delegate.Profile do |> done() end - defp register_github_user(github_profile) do + defp register_github_user(github_profile, remote_ip) do Multi.new() |> Multi.run(:create_user, fn _ -> create_user(github_profile, :github) @@ -94,11 +94,26 @@ defmodule MastaniServer.Accounts.Delegate.Profile do |> Multi.run(:create_profile, fn %{create_user: user} -> create_profile(user, github_profile, :github) end) + |> Multi.run(:init_achievement, fn %{create_user: user} -> + Achievement |> ORM.upsert_by([user_id: user.id], %{user_id: user.id}) + end) |> Repo.transaction() - |> register_github_result() + |> register_github_result(remote_ip) end - defp register_github_result({:ok, %{create_user: user}}), do: token_info(user) + defp register_github_result({:ok, %{create_user: user}}, remote_ip) do + # ignore error + case RadarSearch.locate_city(remote_ip) do + {:ok, city} -> + update_profile(user, %{geo_city: city}) + + # IO.inspect("location search error") + {:error, _} -> + {:ok, "pass"} + end + + token_info(user) + end defp register_github_result({:error, :create_user, _result, _steps}), do: {:error, "Accounts create_user internal error"} diff --git a/lib/mastani_server/accounts/delegates/publish.ex b/lib/mastani_server/accounts/delegates/publish.ex new file mode 100644 index 000000000..d395a4e3c --- /dev/null +++ b/lib/mastani_server/accounts/delegates/publish.ex @@ -0,0 +1,49 @@ +defmodule MastaniServer.Accounts.Delegate.Publish do + @moduledoc """ + user followers / following related + """ + import Ecto.Query, warn: false + import Helper.Utils, only: [done: 1] + # import Helper.ErrorCode + import ShortMaps + + import MastaniServer.CMS.Utils.Matcher + + alias Helper.{ORM, QueryBuilder} + # alias MastaniServer.{Accounts, Repo} + + alias MastaniServer.Accounts.User + # alias MastaniServer.CMS + + @doc """ + get paged published contets of a user + """ + def published_contents(%User{id: user_id}, thread, %{page: page, size: size} = filter) do + with {:ok, user} <- ORM.find(User, user_id), + {:ok, content} <- match_action(thread, :self) do + content.target + |> join(:inner, [content], author in assoc(content, :author)) + |> where([content, author], author.user_id == ^user.id) + |> select([content, author], content) + |> QueryBuilder.filter_pack(filter) + |> ORM.paginater(~m(page size)a) + |> done() + end + end + + @doc """ + get paged published comments of a user + """ + def published_comments(%User{id: user_id}, thread, %{page: page, size: size} = filter) do + with {:ok, user} <- ORM.find(User, user_id), + {:ok, content} <- match_action(thread, :comment) do + content.reactor + |> join(:inner, [comment], author in assoc(comment, :author)) + |> where([comment, author], author.id == ^user.id) + |> select([comment, author], comment) + |> QueryBuilder.filter_pack(filter) + |> ORM.paginater(~m(page size)a) + |> done() + end + end +end diff --git a/lib/mastani_server/accounts/delegates/reacted_contents.ex b/lib/mastani_server/accounts/delegates/reacted_contents.ex index 49516e7b9..68de41988 100644 --- a/lib/mastani_server/accounts/delegates/reacted_contents.ex +++ b/lib/mastani_server/accounts/delegates/reacted_contents.ex @@ -10,6 +10,26 @@ defmodule MastaniServer.Accounts.Delegate.ReactedContents do alias Helper.{ORM, QueryBuilder} alias MastaniServer.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 diff --git a/lib/mastani_server/accounts/favorite_category.ex b/lib/mastani_server/accounts/favorite_category.ex index 9da0ecfcd..15f829173 100644 --- a/lib/mastani_server/accounts/favorite_category.ex +++ b/lib/mastani_server/accounts/favorite_category.ex @@ -7,11 +7,11 @@ defmodule MastaniServer.Accounts.FavoriteCategory do alias MastaniServer.Accounts.User @required_fields ~w(user_id title)a - @optional_fields ~w(index total_count private desc)a + @optional_fields ~w(index total_count private desc last_updated)a @type t :: %FavoriteCategory{} schema "favorite_categories" do - belongs_to(:user, Accounts.User, foreign_key: :user_id) + belongs_to(:user, User, foreign_key: :user_id) # has_many(:posts, ...) field(:title, :string) @@ -19,6 +19,8 @@ defmodule MastaniServer.Accounts.FavoriteCategory do 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 diff --git a/lib/mastani_server/accounts/source_contribute.ex b/lib/mastani_server/accounts/source_contribute.ex new file mode 100644 index 000000000..da86e0fed --- /dev/null +++ b/lib/mastani_server/accounts/source_contribute.ex @@ -0,0 +1,24 @@ +defmodule MastaniServer.Accounts.SourceContribute do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + + @optional_fields ~w(web server mobile we_app h5)a + + @type t :: %SourceContribute{} + embedded_schema do + field(:web, :boolean) + field(:server, :boolean) + field(:mobile, :boolean) + field(:we_app, :boolean) + field(:h5, :boolean) + end + + @doc false + def changeset(%SourceContribute{} = source_contribute, attrs) do + source_contribute + |> cast(attrs, @optional_fields) + end +end diff --git a/lib/mastani_server/accounts/user.ex b/lib/mastani_server/accounts/user.ex index 5b0550236..d0d430ba7 100644 --- a/lib/mastani_server/accounts/user.ex +++ b/lib/mastani_server/accounts/user.ex @@ -34,6 +34,9 @@ defmodule MastaniServer.Accounts.User do field(:email, :string) field(:location, :string) field(:from_github, :boolean) + field(:geo_city, :string) + + field(:views, :integer, default: 0) sscial_fields() @@ -48,8 +51,17 @@ defmodule MastaniServer.Accounts.User do has_many(:followings, {"users_followings", UserFollowing}) 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}) + has_many(:stared_videos, {"videos_stars", CMS.VideoStar}) + + # favorited contents has_many(:favorited_posts, {"posts_favorites", CMS.PostFavorite}) has_many(:favorited_jobs, {"jobs_favorites", CMS.JobFavorite}) + has_many(:favorited_videos, {"videos_favorites", CMS.VideoFavorite}) + has_many(:favorited_repos, {"repos_favorites", CMS.RepoFavorite}) has_many(:favorite_categories, {"favorite_categories", FavoriteCategory}) diff --git a/lib/mastani_server/accounts/utils/loader.ex b/lib/mastani_server/accounts/utils/loader.ex index 83d5a02e6..817e2cc83 100644 --- a/lib/mastani_server/accounts/utils/loader.ex +++ b/lib/mastani_server/accounts/utils/loader.ex @@ -24,10 +24,11 @@ defmodule MastaniServer.Accounts.Utils.Loader do |> select([u, c], c) end + # TODO: fix later, this is not working def query({"users_followers", UserFollower}, %{count: _}) do UserFollower |> group_by([f], f.user_id) - |> select([f], count(f.id)) + |> select([f], count(f.follower_id)) end def query({"users_followings", UserFollowing}, %{count: _}) do @@ -40,17 +41,39 @@ defmodule MastaniServer.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 + + def query({"videos_stars", CMS.VideoStar}, %{count: _}) do + CMS.VideoStar |> count_contents + end + + # favorited contents count def query({"posts_favorites", CMS.PostFavorite}, %{count: _}) do - CMS.PostFavorite |> count_cotents + CMS.PostFavorite |> count_contents end def query({"jobs_favorites", CMS.JobFavorite}, %{count: _}) do - CMS.JobFavorite |> count_cotents + CMS.JobFavorite |> count_contents + end + + def query({"videos_favorites", CMS.VideoFavorite}, %{count: _}) do + CMS.VideoFavorite |> count_contents + end + + def query({"repos_favorites", CMS.RepoFavorite}, %{count: _}) do + CMS.RepoFavorite |> count_contents end def query(queryable, _args), do: queryable - defp count_cotents(queryable) do + defp count_contents(queryable) do queryable |> group_by([f], f.user_id) |> select([f], count(f.id)) diff --git a/lib/mastani_server/cms/cms.ex b/lib/mastani_server/cms/cms.ex index 3f127b0c7..c1b3bd5b3 100644 --- a/lib/mastani_server/cms/cms.ex +++ b/lib/mastani_server/cms/cms.ex @@ -8,7 +8,9 @@ defmodule MastaniServer.CMS do ArticleCURD, ArticleOperation, ArticleReaction, + FavoritedContents, CommentCURD, + CommunitySync, CommentReaction, CommunityCURD, CommunityOperation, @@ -21,6 +23,8 @@ defmodule MastaniServer.CMS do # Community CURD: editors, thread, tag # >> editor .. defdelegate update_editor(user, community, title), to: CommunityCURD + # >> geo info .. + defdelegate community_geo_info(community), to: CommunityCURD # >> subscribers / editors defdelegate community_members(type, community, filters), to: CommunityCURD # >> category @@ -32,7 +36,13 @@ defmodule MastaniServer.CMS do defdelegate create_tag(thread, attrs, user), to: CommunityCURD defdelegate update_tag(attrs), to: CommunityCURD defdelegate get_tags(community, thread), to: CommunityCURD + defdelegate get_tags(community, thread, topic), to: CommunityCURD defdelegate get_tags(filter), to: CommunityCURD + # >> wiki & cheatsheet (sync with github) + defdelegate get_wiki(community), to: CommunitySync + defdelegate get_cheatsheet(community), to: CommunitySync + defdelegate sync_github_content(community, thread, attrs), to: CommunitySync + defdelegate add_contributor(content, attrs), to: CommunitySync # CommunityOperation # >> category @@ -46,9 +56,12 @@ defmodule MastaniServer.CMS do defdelegate unset_thread(community, thread), to: CommunityOperation # >> subscribe / unsubscribe defdelegate subscribe_community(community, user), to: CommunityOperation + defdelegate subscribe_community(community, user, remote_ip), to: CommunityOperation defdelegate unsubscribe_community(community, user), to: CommunityOperation + defdelegate unsubscribe_community(community, user, remote_ip), to: CommunityOperation # ArticleCURD + defdelegate read_content(thread, id, user), to: ArticleCURD defdelegate paged_contents(queryable, filter), to: ArticleCURD defdelegate create_content(community, thread, attrs, user), to: ArticleCURD defdelegate reaction_users(thread, react, id, filters), to: ArticleCURD @@ -57,9 +70,18 @@ defmodule MastaniServer.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 # ArticleOperation # >> set flag on article, like: pin / unpin article defdelegate set_community_flags(queryable, community_id, attrs), to: ArticleOperation + defdelegate pin_content(queryable, community_id, topic), to: ArticleOperation + defdelegate undo_pin_content(queryable, community_id, topic), to: ArticleOperation + defdelegate pin_content(queryable, community_id), to: ArticleOperation + defdelegate undo_pin_content(queryable, community_id), to: ArticleOperation + + # defdelegate pin_content(queryable, community_id, thread), to: ArticleOperation + # defdelegate undo_pin_content(queryable, community_id, thread, topic), to: ArticleOperation + # defdelegate undo_pin_content(queryable, community_id, thread), to: ArticleOperation # >> tag: set / unset defdelegate set_tag(community, thread, tag, content_id), to: ArticleOperation @@ -69,9 +91,11 @@ defmodule MastaniServer.CMS do defdelegate unset_community(community, thread, content_id), to: ArticleOperation # Comment CURD + defdelegate list_comments(thread, content_id, filters), to: CommentCURD + defdelegate list_comments_participators(thread, content_id, filters), to: CommentCURD + defdelegate create_comment(thread, content_id, body, user), to: CommentCURD defdelegate delete_comment(thread, content_id), to: CommentCURD - defdelegate list_comments(thread, content_id, filters), to: CommentCURD defdelegate list_replies(thread, comment, user), to: CommentCURD defdelegate reply_comment(thread, comment, body, user), to: CommentCURD diff --git a/lib/mastani_server/cms/community.ex b/lib/mastani_server/cms/community.ex index c402bd99a..f5b88d0fc 100644 --- a/lib/mastani_server/cms/community.ex +++ b/lib/mastani_server/cms/community.ex @@ -12,14 +12,16 @@ defmodule MastaniServer.CMS.Community do Job, CommunityThread, CommunitySubscriber, - CommunityEditor + CommunityEditor, + CommunityWiki, + CommunityCheatsheet } alias MastaniServer.Accounts @required_fields ~w(title desc user_id logo raw)a # @required_fields ~w(title desc user_id)a - @optional_fields ~w(label)a + @optional_fields ~w(label geo_info)a schema "communities" do field(:title, :string) @@ -28,6 +30,7 @@ defmodule MastaniServer.CMS.Community do # field(:category, :string) field(:label, :string) field(:raw, :string) + field(:geo_info, :map) belongs_to(:author, Accounts.User, foreign_key: :user_id) @@ -35,6 +38,9 @@ defmodule MastaniServer.CMS.Community do has_many(:subscribers, {"communities_subscribers", CommunitySubscriber}) has_many(:editors, {"communities_editors", CommunityEditor}) + has_one(:wiki, CommunityWiki) + has_one(:cheatsheet, CommunityCheatsheet) + many_to_many( :categories, Category, diff --git a/lib/mastani_server/cms/community_cheatsheet.ex b/lib/mastani_server/cms/community_cheatsheet.ex new file mode 100644 index 000000000..6dbf3d923 --- /dev/null +++ b/lib/mastani_server/cms/community_cheatsheet.ex @@ -0,0 +1,33 @@ +defmodule MastaniServer.CMS.CommunityCheatsheet do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + + alias MastaniServer.CMS.{Community, GithubContributor} + + @required_fields ~w(community_id last_sync)a + @optional_fields ~w(readme)a + + @type t :: %CommunityCheatsheet{} + schema "community_cheatsheets" do + belongs_to(:community, Community) + + field(:readme, :string) + embeds_many(:contributors, GithubContributor, on_replace: :delete) + field(:last_sync, :utc_datetime) + + field(:views, :integer, default: 0) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%CommunityCheatsheet{} = community_cheatsheet, attrs) do + community_cheatsheet + |> cast(attrs, @required_fields ++ @optional_fields) + |> cast_embed(:contributors, with: &GithubContributor.changeset/2) + |> validate_required(@required_fields) + end +end diff --git a/lib/mastani_server/cms/community_wiki.ex b/lib/mastani_server/cms/community_wiki.ex new file mode 100644 index 000000000..ac85f6da6 --- /dev/null +++ b/lib/mastani_server/cms/community_wiki.ex @@ -0,0 +1,33 @@ +defmodule MastaniServer.CMS.CommunityWiki do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + + alias MastaniServer.CMS.{Community, GithubContributor} + + @required_fields ~w(community_id last_sync)a + @optional_fields ~w(readme)a + + @type t :: %CommunityWiki{} + schema "community_wikis" do + belongs_to(:community, Community) + + field(:readme, :string) + embeds_many(:contributors, GithubContributor, on_replace: :delete) + field(:last_sync, :utc_datetime) + + field(:views, :integer, default: 0) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%CommunityWiki{} = community_wiki, attrs) do + community_wiki + |> cast(attrs, @required_fields ++ @optional_fields) + |> cast_embed(:contributors, with: &GithubContributor.changeset/2) + |> validate_required(@required_fields) + end +end diff --git a/lib/mastani_server/cms/delegates/article_curd.ex b/lib/mastani_server/cms/delegates/article_curd.ex index 08b917688..eef73cc85 100644 --- a/lib/mastani_server/cms/delegates/article_curd.ex +++ b/lib/mastani_server/cms/delegates/article_curd.ex @@ -8,15 +8,34 @@ defmodule MastaniServer.CMS.Delegate.ArticleCURD do import Helper.ErrorCode import ShortMaps + alias MastaniServer.Repo + alias MastaniServer.Accounts.User - alias MastaniServer.{CMS, Repo, Statistics} + alias MastaniServer.{CMS, Statistics} alias CMS.Delegate.ArticleOperation alias Helper.{ORM, QueryBuilder} - alias CMS.{Author, Community, Tag} + alias CMS.{Author, Community, Tag, Topic} alias Ecto.Multi + @doc """ + login user read cms content by add views count and viewer record + """ + def read_content(thread, id, %User{id: user_id}) do + condition = %{user_id: user_id} |> Map.merge(content_id(thread, id)) + + with {:ok, action} <- match_action(thread, :self), + {:ok, _viewer} <- action.viewer |> ORM.findby_or_insert(condition, condition) do + action.target |> ORM.read(id, inc: :views) + end + end + + defp content_id(:post, id), do: %{post_id: id} + defp content_id(:job, id), do: %{job_id: id} + defp content_id(:repo, id), do: %{repo_id: id} + defp content_id(:video, id), do: %{video_id: id} + @doc """ get paged post / job ... """ @@ -27,66 +46,6 @@ defmodule MastaniServer.CMS.Delegate.ArticleCURD do |> add_pin_contents_ifneed(queryable, filter) end - defp flag_query(queryable, filter, flag \\ %{}) do - flag = %{pin: false, trash: false} |> Map.merge(flag) - - # NOTE: this case judge is used for test case - case filter |> Map.has_key?(:community) do - true -> - queryable - |> join(:inner, [q], f in assoc(q, :community_flags)) - |> where([q, f], f.pin == ^flag.pin and f.trash == ^flag.trash) - |> join(:inner, [q, f], c in assoc(f, :community)) - |> where([q, f, c], c.raw == ^filter.community) - - false -> - queryable - end - end - - # only first page need pin contents - defp add_pin_contents_ifneed(contents, queryable, filter) do - with {:ok, normal_contents} <- contents, - true <- Map.has_key?(filter, :community), - true <- 1 == Map.get(normal_contents, :page_number) do - {:ok, pined_content} = - queryable - |> flag_query(filter, %{pin: true}) - |> ORM.find_all(filter) - - # TODO: add hot post pin/trash state ? - # don't by flag_changeset, dataloader make things complex - concat_contents(pined_content, normal_contents) - else - _error -> - contents - end - end - - defp concat_contents(pined_content, normal_contents) do - case pined_content |> Map.get(:total_count) do - 0 -> - {:ok, normal_contents} - - _ -> - # NOTE: this is tricy, should use dataloader refactor - pind_entries = - pined_content - |> Map.get(:entries) - |> Enum.map(&struct(&1, %{pin: true})) - - normal_entries = normal_contents |> Map.get(:entries) - - normal_count = normal_contents |> Map.get(:total_count) - pind_count = pined_content |> Map.get(:total_count) - - normal_contents - |> Map.put(:entries, pind_entries ++ normal_entries) - |> Map.put(:total_count, pind_count + normal_count) - |> done - end - end - @doc """ Creates a content(post/job ...), and set community. @@ -99,7 +58,12 @@ defmodule MastaniServer.CMS.Delegate.ArticleCURD do {:error, %Ecto.Changeset{}} """ - def create_content(%Community{id: community_id}, thread, attrs, %User{id: user_id}) do + def create_content( + %Community{id: community_id}, + thread, + attrs, + %User{id: user_id} + ) do with {:ok, author} <- ensure_author_exists(%User{id: user_id}), {:ok, action} <- match_action(thread, :community), {:ok, community} <- ORM.find(Community, community_id) do @@ -114,12 +78,20 @@ defmodule MastaniServer.CMS.Delegate.ArticleCURD do |> Multi.run(:set_community, fn %{add_content_author: content} -> ArticleOperation.set_community(community, thread, content.id) end) + |> Multi.run(:set_topic, fn %{add_content_author: content} -> + topic_title = + case attrs |> Map.has_key?(:topic) do + true -> attrs.topic + false -> "posts" + end + + ArticleOperation.set_topic(%Topic{title: topic_title}, thread, content.id) + end) |> Multi.run(:set_community_flag, fn %{add_content_author: content} -> # TODO: remove this judge, as content should have a flag case action |> Map.has_key?(:flag) do true -> ArticleOperation.set_community_flags(content, community.id, %{ - pin: false, trash: false }) @@ -143,8 +115,9 @@ defmodule MastaniServer.CMS.Delegate.ArticleCURD do defp create_content_result({:ok, %{add_content_author: result}}), do: {:ok, result} + # TODO: need more spec error handle defp create_content_result({:error, :add_content_author, _result, _steps}) do - {:error, [message: "assign author", code: ecode(:create_fails)]} + {:error, [message: "create cms content author", code: ecode(:create_fails)]} end defp create_content_result({:error, :set_community, _result, _steps}) do @@ -155,6 +128,11 @@ defmodule MastaniServer.CMS.Delegate.ArticleCURD do {:error, [message: "set community flag", code: ecode(:create_fails)]} end + defp create_content_result({:error, :set_topic, result, _steps}) do + IO.inspect(result, label: "set topic") + {:error, [message: "set topic", code: ecode(:create_fails)]} + end + defp create_content_result({:error, :set_tag, result, _steps}) do {:error, result} end @@ -216,4 +194,107 @@ defmodule MastaniServer.CMS.Delegate.ArticleCURD do defp handle_existing_author({:error, changeset}) do ORM.find_by(Author, user_id: changeset.data.user_id) end + + defp flag_query(queryable, filter, flag \\ %{}) do + flag = %{trash: false} |> Map.merge(flag) + + # NOTE: this case judge is used for test case + case filter |> Map.has_key?(:community) do + true -> + queryable + |> join(:inner, [q], f in assoc(q, :community_flags)) + |> where([q, f], f.trash == ^flag.trash) + |> join(:inner, [q, f], c in assoc(f, :community)) + |> where([q, f, c], c.raw == ^filter.community) + + false -> + queryable + end + end + + # only first page need pin contents + # TODO: use seperate pined table, which is much more smaller + defp add_pin_contents_ifneed(contents, CMS.Post, %{community: community} = filter) do + with {:ok, normal_contents} <- contents, + true <- Map.has_key?(filter, :community), + true <- 1 == Map.get(normal_contents, :page_number) do + {:ok, pined_content} = + CMS.PinedPost + |> join(:inner, [p], c in assoc(p, :community)) + |> join(:inner, [p], t in assoc(p, :topic)) + |> join(:inner, [p], content in assoc(p, :post)) + |> where( + [p, c, t, content], + c.raw == ^filter.community and t.raw == ^Map.get(filter, :topic, "posts") + ) + |> select([p, c, t, content], content) + # 10 pined contents per community/thread, at most + |> ORM.paginater(%{page: 1, size: 10}) + |> done() + + concat_contents(pined_content, normal_contents) + else + _error -> + contents + end + end + + defp add_pin_contents_ifneed(contents, CMS.Job, %{community: community} = filter) do + merge_pin_contents(contents, :job, CMS.PinedJob, filter) + end + + defp add_pin_contents_ifneed(contents, CMS.Video, %{community: community} = filter) do + merge_pin_contents(contents, :video, CMS.PinedVideo, filter) + end + + defp add_pin_contents_ifneed(contents, CMS.Repo, %{community: community} = filter) do + merge_pin_contents(contents, :repo, CMS.PinedRepo, filter) + end + + defp merge_pin_contents(contents, thread, pin_schema, %{community: community} = filter) do + with {:ok, normal_contents} <- contents, + true <- Map.has_key?(filter, :community), + true <- 1 == Map.get(normal_contents, :page_number) do + {:ok, pined_content} = + pin_schema + |> join(:inner, [p], c in assoc(p, :community)) + |> join(:inner, [p], content in assoc(p, ^thread)) + |> where([p, c, content], c.raw == ^filter.community) + |> select([p, c, content], content) + # 10 pined contents per community/thread, at most + |> ORM.paginater(%{page: 1, size: 10}) + |> done() + + concat_contents(pined_content, normal_contents) + else + _error -> + contents + end + end + + defp add_pin_contents_ifneed(contents, _querable, _filter), do: contents + + defp concat_contents(pined_content, normal_contents) do + case pined_content |> Map.get(:total_count) do + 0 -> + {:ok, normal_contents} + + _ -> + # NOTE: this is tricy, should use dataloader refactor + pind_entries = + pined_content + |> Map.get(:entries) + |> Enum.map(&struct(&1, %{pin: true})) + + normal_entries = normal_contents |> Map.get(:entries) + + normal_count = normal_contents |> Map.get(:total_count) + pind_count = pined_content |> Map.get(:total_count) + + normal_contents + |> Map.put(:entries, pind_entries ++ normal_entries) + |> Map.put(:total_count, pind_count + normal_count) + |> done + end + end end diff --git a/lib/mastani_server/cms/delegates/article_operation.ex b/lib/mastani_server/cms/delegates/article_operation.ex index 2268cf151..34fe572ae 100644 --- a/lib/mastani_server/cms/delegates/article_operation.ex +++ b/lib/mastani_server/cms/delegates/article_operation.ex @@ -18,14 +18,84 @@ defmodule MastaniServer.CMS.Delegate.ArticleOperation do RepoCommunityFlag, Video, VideoCommunityFlag, - Tag + Tag, + Topic, + PinedPost, + PinedJob, + PinedVideo, + PinedRepo } alias MastaniServer.CMS.Repo, as: CMSRepo alias MastaniServer.Repo + def pin_content(%Post{id: post_id}, %Community{id: community_id}, topic) do + with {:ok, %{id: topic_id}} <- ORM.find_by(Topic, %{raw: topic}), + {:ok, pined} <- + ORM.findby_or_insert( + PinedPost, + ~m(post_id community_id topic_id)a, + ~m(post_id community_id topic_id)a + ) do + Post |> ORM.find(pined.post_id) + end + end + + def undo_pin_content(%Post{id: post_id}, %Community{id: community_id}, topic) do + with {:ok, %{id: topic_id}} <- ORM.find_by(Topic, %{raw: topic}), + {:ok, pined} <- ORM.find_by(PinedPost, ~m(post_id community_id topic_id)a), + {:ok, deleted} <- ORM.delete(pined) do + Post |> ORM.find(deleted.post_id) + end + end + + def pin_content(%Job{id: job_id}, %Community{id: community_id}) do + attrs = ~m(job_id community_id)a + + with {:ok, pined} <- ORM.findby_or_insert(PinedJob, attrs, attrs) do + Job |> ORM.find(pined.job_id) + end + end + + def undo_pin_content(%Job{id: job_id}, %Community{id: community_id}) do + with {:ok, pined} <- ORM.find_by(PinedJob, ~m(job_id community_id)a), + {:ok, deleted} <- ORM.delete(pined) do + Job |> ORM.find(deleted.job_id) + end + end + + def pin_content(%Video{id: video_id}, %Community{id: community_id}) do + attrs = ~m(video_id community_id)a + + with {:ok, pined} <- ORM.findby_or_insert(PinedVideo, attrs, attrs) do + Video |> ORM.find(pined.video_id) + end + end + + def undo_pin_content(%Video{id: video_id}, %Community{id: community_id}) do + with {:ok, pined} <- ORM.find_by(PinedVideo, ~m(video_id community_id)a), + {:ok, deleted} <- ORM.delete(pined) do + Video |> ORM.find(deleted.video_id) + end + end + + def pin_content(%CMSRepo{id: repo_id}, %Community{id: community_id}) do + attrs = ~m(repo_id community_id)a + + with {:ok, pined} <- ORM.findby_or_insert(PinedRepo, attrs, attrs) do + CMSRepo |> ORM.find(pined.repo_id) + end + end + + def undo_pin_content(%CMSRepo{id: repo_id}, %Community{id: community_id}) do + with {:ok, pined} <- ORM.find_by(PinedRepo, ~m(repo_id community_id)a), + {:ok, deleted} <- ORM.delete(pined) do + CMSRepo |> ORM.find(deleted.repo_id) + end + end + @doc """ - pin / unpin, trash / untrash articles + trash / untrash articles """ def set_community_flags(%Post{id: _} = content, community_id, attrs), do: do_set_flag(content, community_id, attrs) @@ -43,7 +113,7 @@ defmodule MastaniServer.CMS.Delegate.ArticleOperation do with {:ok, content} <- ORM.find(content.__struct__, content.id), {:ok, community} <- ORM.find(Community, community_id), {:ok, record} <- insert_flag_record(content, community.id, attrs) do - {:ok, struct(content, %{pin: record.pin, trash: record.trash})} + {:ok, struct(content, %{trash: record.trash})} end end @@ -131,6 +201,26 @@ defmodule MastaniServer.CMS.Delegate.ArticleOperation do end end + @doc """ + set topic only for post + """ + def set_topic(%Topic{title: title}, :post, content_id) do + with {:ok, content} <- ORM.find(Post, content_id, preload: :topics), + {:ok, topic} <- + ORM.findby_or_insert(Topic, %{title: title}, %{ + title: title, + thread: "post", + raw: title + }) do + content + |> Ecto.Changeset.change() + |> Ecto.Changeset.put_assoc(:topics, content.topics ++ [topic]) + |> Repo.update() + end + end + + def set_topic(_topic, _thread, _content_id), do: {:ok, :pass} + # make sure the reuest tag is in the current community thread # example: you can't set a other thread tag to this thread's article defp tag_in_community_thread?(%Community{id: communityId}, thread, tag) do diff --git a/lib/mastani_server/cms/delegates/article_reaction.ex b/lib/mastani_server/cms/delegates/article_reaction.ex index af1f290cb..ee0713bb4 100644 --- a/lib/mastani_server/cms/delegates/article_reaction.ex +++ b/lib/mastani_server/cms/delegates/article_reaction.ex @@ -36,8 +36,9 @@ defmodule MastaniServer.CMS.Delegate.ArticleReaction do defp reaction_result({:ok, %{create_reaction_record: result}}), do: result |> done() - defp reaction_result({:error, :create_reaction_record, _result, _steps}), - do: {:error, [message: "create reaction fails", code: ecode(:react_fails)]} + defp reaction_result({:error, :create_reaction_record, _result, _steps}) do + {:error, [message: "create reaction fails", code: ecode(:react_fails)]} + end defp reaction_result({:error, :add_achievement, _result, _steps}), do: {:error, [message: "achieve fails", code: ecode(:react_fails)]} @@ -74,8 +75,9 @@ defmodule MastaniServer.CMS.Delegate.ArticleReaction do defp undo_reaction_result({:ok, %{delete_reaction_record: result}}), do: result |> done() - defp undo_reaction_result({:error, :delete_reaction_record, _result, _steps}), - do: {:error, [message: "delete reaction fails", code: ecode(:react_fails)]} + defp undo_reaction_result({:error, :delete_reaction_record, _result, _steps}) do + {:error, [message: "delete reaction fails", code: ecode(:react_fails)]} + end defp undo_reaction_result({:error, :minus_achievement, _result, _steps}), do: {:error, [message: "achieve fails", code: ecode(:react_fails)]} diff --git a/lib/mastani_server/cms/delegates/comment_curd.ex b/lib/mastani_server/cms/delegates/comment_curd.ex index a0e637706..c111ae998 100644 --- a/lib/mastani_server/cms/delegates/comment_curd.ex +++ b/lib/mastani_server/cms/delegates/comment_curd.ex @@ -9,9 +9,10 @@ defmodule MastaniServer.CMS.Delegate.CommentCURD do import MastaniServer.CMS.Utils.Matcher import ShortMaps - alias MastaniServer.{Repo, Accounts} alias Helper.{ORM, QueryBuilder} - alias MastaniServer.CMS.{PostCommentReply, JobCommentReply} + alias MastaniServer.{Repo, Accounts} + + alias MastaniServer.CMS.{PostCommentReply, JobCommentReply, VideoCommentReply, RepoCommentReply} alias Ecto.Multi @@ -36,9 +37,6 @@ defmodule MastaniServer.CMS.Delegate.CommentCURD do end end - defp merge_comment_attrs(:post, attrs, id), do: attrs |> Map.merge(%{post_id: id}) - defp merge_comment_attrs(:job, attrs, id), do: attrs |> Map.merge(%{job_id: id}) - @doc """ Delete the comment and increase all the floor after this comment """ @@ -50,16 +48,14 @@ defmodule MastaniServer.CMS.Delegate.CommentCURD do ORM.delete(comment) end) |> Multi.run(:update_floor, fn _ -> - ret = - Repo.update_all( - from(p in action.reactor, where: p.id > ^comment.id), - inc: [floor: -1] - ) - |> done() - - case ret do + Repo.update_all( + from(p in action.reactor, where: p.id > ^comment.id), + inc: [floor: -1] + ) + |> done() + |> case do {:ok, _} -> {:ok, comment} - _ -> {:error, ""} + {:error, _} -> {:error, ""} end end) |> Repo.transaction() @@ -77,6 +73,9 @@ defmodule MastaniServer.CMS.Delegate.CommentCURD do {:error, [message: "update follor fails", code: ecode(:delete_fails)]} end + @doc """ + list paged comments + """ def list_comments(thread, content_id, %{page: page, size: size} = filters) do with {:ok, action} <- match_action(thread, :comment) do dynamic = dynamic_comment_where(thread, content_id) @@ -89,6 +88,24 @@ defmodule MastaniServer.CMS.Delegate.CommentCURD do end end + @doc """ + list paged comments participators + """ + def list_comments_participators(thread, content_id, %{page: page, size: size} = filters) do + with {:ok, action} <- match_action(thread, :comment) do + dynamic = dynamic_comment_where(thread, content_id) + + action.reactor + |> where(^dynamic) + |> QueryBuilder.filter_pack(filters) + |> join(:inner, [c], a in assoc(c, :author)) + |> distinct([c, a], a.id) + |> select([c, a], a) + |> ORM.paginater(~m(page size)a) + |> done() + end + end + def list_replies(thread, comment_id, %Accounts.User{id: user_id}) do with {:ok, action} <- match_action(thread, :comment) do action.reactor @@ -113,17 +130,14 @@ defmodule MastaniServer.CMS.Delegate.CommentCURD do } attrs = merge_reply_attrs(thread, attrs, comment) - brige_reply(thread, action.reactor, comment, attrs) + bridge_reply(thread, action.reactor, comment, attrs) end end - defp merge_reply_attrs(:post, attrs, comment), - do: attrs |> Map.merge(%{post_id: comment.post_id}) - - defp merge_reply_attrs(:job, attrs, comment), do: attrs |> Map.merge(%{job_id: comment.job_id}) - - defp brige_reply(:post, queryable, comment, attrs) do - # TODO: use Multi task to refactor + # simulate a join connection + # TODO: use Multi task to refactor + # TODO: refactor boilerplate code + defp bridge_reply(:post, queryable, comment, attrs) do with {:ok, reply} <- ORM.create(queryable, attrs) do ORM.update(reply, %{reply_id: comment.id}) @@ -134,8 +148,7 @@ defmodule MastaniServer.CMS.Delegate.CommentCURD do end end - defp brige_reply(:job, queryable, comment, attrs) do - # TODO: use Multi task to refactor + defp bridge_reply(:job, queryable, comment, attrs) do with {:ok, reply} <- ORM.create(queryable, attrs) do ORM.update(reply, %{reply_id: comment.id}) @@ -145,6 +158,28 @@ defmodule MastaniServer.CMS.Delegate.CommentCURD do end end + defp bridge_reply(:video, queryable, comment, attrs) do + with {:ok, reply} <- ORM.create(queryable, attrs) do + ORM.update(reply, %{reply_id: comment.id}) + + {:ok, _} = + VideoCommentReply |> ORM.create(%{video_comment_id: comment.id, reply_id: reply.id}) + + queryable |> ORM.find(reply.id) + end + end + + defp bridge_reply(:repo, queryable, comment, attrs) do + with {:ok, reply} <- ORM.create(queryable, attrs) do + ORM.update(reply, %{reply_id: comment.id}) + + {:ok, _} = + RepoCommentReply |> ORM.create(%{repo_comment_id: comment.id, reply_id: reply.id}) + + queryable |> ORM.find(reply.id) + end + end + # for create comment defp get_next_floor(thread, queryable, id) when is_integer(id) do dynamic = dynamic_comment_where(thread, id) @@ -163,9 +198,30 @@ defmodule MastaniServer.CMS.Delegate.CommentCURD do |> ORM.next_count() end + # merge_comment_attrs when create comemnt + defp merge_comment_attrs(:post, attrs, id), do: attrs |> Map.merge(%{post_id: id}) + defp merge_comment_attrs(:job, attrs, id), do: attrs |> Map.merge(%{job_id: id}) + defp merge_comment_attrs(:video, attrs, id), do: attrs |> Map.merge(%{video_id: id}) + defp merge_comment_attrs(:repo, attrs, id), do: attrs |> Map.merge(%{repo_id: id}) + + defp merge_reply_attrs(:post, attrs, comment), + do: attrs |> Map.merge(%{post_id: comment.post_id}) + + defp merge_reply_attrs(:job, attrs, comment), do: attrs |> Map.merge(%{job_id: comment.job_id}) + + defp merge_reply_attrs(:video, attrs, comment), + do: attrs |> Map.merge(%{video_id: comment.video_id}) + + defp merge_reply_attrs(:repo, attrs, comment), + do: attrs |> Map.merge(%{repo_id: comment.repo_id}) + defp dynamic_comment_where(:post, id), do: dynamic([c], c.post_id == ^id) defp dynamic_comment_where(:job, id), do: dynamic([c], c.job_id == ^id) + defp dynamic_comment_where(:video, id), do: dynamic([c], c.video_id == ^id) + defp dynamic_comment_where(:repo, id), do: dynamic([c], c.repo_id == ^id) defp dynamic_reply_where(:post, comment), do: dynamic([c], c.post_id == ^comment.post_id) defp dynamic_reply_where(:job, comment), do: dynamic([c], c.job_id == ^comment.job_id) + defp dynamic_reply_where(:video, comment), do: dynamic([c], c.video_id == ^comment.video_id) + defp dynamic_reply_where(:repo, comment), do: dynamic([c], c.repo_id == ^comment.repo_id) end diff --git a/lib/mastani_server/cms/delegates/comment_reaction.ex b/lib/mastani_server/cms/delegates/comment_reaction.ex index 8c68a97a5..a478d2d86 100644 --- a/lib/mastani_server/cms/delegates/comment_reaction.ex +++ b/lib/mastani_server/cms/delegates/comment_reaction.ex @@ -1,8 +1,8 @@ defmodule MastaniServer.CMS.Delegate.CommentReaction do import MastaniServer.CMS.Utils.Matcher - alias MastaniServer.Accounts alias Helper.ORM + alias MastaniServer.Accounts def like_comment(thread, comment_id, %Accounts.User{id: user_id}) do feel_comment(thread, comment_id, user_id, :like) @@ -20,10 +20,16 @@ defmodule MastaniServer.CMS.Delegate.CommentReaction do undo_feel_comment(thread, comment_id, user_id, :dislike) 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(:video_comment, comment_id), do: %{video_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) when valid_feeling(feeling) do with {:ok, action} <- match_action(thread, feeling) do - clause = %{post_comment_id: comment_id, user_id: user_id} + 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, _} -> @@ -39,7 +45,8 @@ defmodule MastaniServer.CMS.Delegate.CommentReaction do defp undo_feel_comment(thread, comment_id, user_id, feeling) do with {:ok, action} <- match_action(thread, feeling) do - clause = %{post_comment_id: comment_id, user_id: user_id} + 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 diff --git a/lib/mastani_server/cms/delegates/community_curd.ex b/lib/mastani_server/cms/delegates/community_curd.ex index c77c5fbfb..639c771ac 100644 --- a/lib/mastani_server/cms/delegates/community_curd.ex +++ b/lib/mastani_server/cms/delegates/community_curd.ex @@ -18,6 +18,7 @@ defmodule MastaniServer.CMS.Delegate.CommunityCURD do CommunityEditor, CommunitySubscriber, Tag, + Topic, Thread } @@ -51,27 +52,51 @@ defmodule MastaniServer.CMS.Delegate.CommunityCURD do @doc """ create a Tag base on type: post / tuts / videos ... """ - def create_tag(thread, attrs, %Accounts.User{id: user_id}) when valid_thread(thread) do + def create_tag(thread, attrs, %Accounts.User{id: user_id}) do with {:ok, action} <- match_action(thread, :tag), {:ok, author} <- ensure_author_exists(%Accounts.User{id: user_id}), - {:ok, _community} <- ORM.find(Community, attrs.community_id) do - attrs = attrs |> Map.merge(%{author_id: author.id}) - attrs = attrs |> map_atom_value(:string) + {:ok, _community} <- ORM.find(Community, attrs.community_id), + {:ok, topic} = find_or_insert_topic(attrs) do + attrs = + attrs + |> Map.merge(%{author_id: author.id, topic_id: topic.id}) + |> map_atom_value(:string) + |> Map.merge(%{thread: attrs.thread |> to_string |> String.downcase()}) action.reactor |> ORM.create(attrs) end end def update_tag(%{id: _id} = attrs) do - attrs = attrs |> map_atom_value(:string) - Tag |> ORM.find_update(%{id: attrs.id, title: attrs.title, color: attrs.color}) + ~m(id title color)a = attrs |> map_atom_value(:string) + + with {:ok, %{id: topic_id}} = find_or_insert_topic(attrs) do + Tag + |> ORM.find_update(~m(id title color color topic_id)a) + end end @doc """ get tags belongs to a community / thread """ + def get_tags(%Community{id: community_id}, thread, topic) when not is_nil(community_id) do + # thread = thread |> to_string |> String.upcase() + # topic = topic |> to_string |> String.upcase() + thread = thread |> to_string |> String.downcase() + topic = topic |> to_string |> String.downcase() + + Tag + |> join(:inner, [t], c in assoc(t, :community)) + |> join(:inner, [t], cp in assoc(t, :topic)) + |> where([t, c, cp], c.id == ^community_id and t.thread == ^thread and cp.title == ^topic) + |> distinct([t], t.title) + |> Repo.all() + |> done() + end + def get_tags(%Community{id: community_id}, thread) when not is_nil(community_id) do - thread = to_string(thread) + # thread = thread |> to_string |> String.upcase() + thread = thread |> to_string |> String.downcase() Tag |> join(:inner, [t], c in assoc(t, :community)) @@ -81,8 +106,8 @@ defmodule MastaniServer.CMS.Delegate.CommunityCURD do |> done() end - def get_tags(%Community{raw: community_raw}, thread) when not is_nil(community_raw) do - thread = to_string(thread) + def get_tags(%Community{raw: community_raw}, thread) do + thread = thread |> to_string |> String.downcase() Tag |> join(:inner, [t], c in assoc(t, :community)) @@ -124,4 +149,40 @@ defmodule MastaniServer.CMS.Delegate.CommunityCURD do Thread |> ORM.create(~m(title raw index)a) end + + @doc """ + return community geo infos + """ + def community_geo_info(%Community{id: community_id}) do + with {:ok, community} <- ORM.find(Community, community_id) do + geo_info_data = + community.geo_info + |> Map.get("data") + |> Enum.map(fn data -> + for {key, val} <- data, into: %{}, do: {String.to_atom(key), val} + end) + |> Enum.reject(&(&1.value <= 0)) + + {:ok, geo_info_data} + end + end + + defp find_or_insert_topic(%{topic: title} = attrs) when is_binary(title) do + title = title |> to_string() |> String.downcase() + thread = attrs.thread |> to_string() |> String.downcase() + + ORM.findby_or_insert(Topic, %{title: title}, %{ + title: title, + thread: thread, + raw: title + }) + end + + defp find_or_insert_topic(%{thread: thread}) do + find_or_insert_topic(%{topic: "index", thread: thread}) + end + + defp find_or_insert_topic(_attrs) do + find_or_insert_topic(%{topic: "index", thread: :post}) + end end diff --git a/lib/mastani_server/cms/delegates/community_operation.ex b/lib/mastani_server/cms/delegates/community_operation.ex index c9989ca35..40f62082d 100644 --- a/lib/mastani_server/cms/delegates/community_operation.ex +++ b/lib/mastani_server/cms/delegates/community_operation.ex @@ -5,7 +5,7 @@ defmodule MastaniServer.CMS.Delegate.CommunityOperation do import ShortMaps alias Ecto.Multi - alias Helper.{Certification, ORM} + alias Helper.{Certification, RadarSearch, ORM} alias MastaniServer.Accounts.User alias MastaniServer.CMS.Delegate.PassportCURD alias MastaniServer.Repo @@ -99,16 +99,75 @@ defmodule MastaniServer.CMS.Delegate.CommunityOperation do @doc """ subscribe a community. (ONLY community, post etc use watch ) """ - def subscribe_community(%Community{id: community_id}, %User{id: user_id}) do + def subscribe_community( + %Community{id: community_id}, + %User{id: user_id}, + remote_ip \\ {127, 0, 0, 1} + ) do + remote_ip = Enum.join(Tuple.to_list(remote_ip), ".") + with {:ok, record} <- CommunitySubscriber |> ORM.create(~m(user_id community_id)a) do + update_geo_info(community_id, user_id, remote_ip, :inc) Community |> ORM.find(record.community_id) end end - def unsubscribe_community(%Community{id: community_id}, %User{id: user_id}) do + def unsubscribe_community( + %Community{id: community_id}, + %User{id: user_id}, + remote_ip \\ {127, 0, 0, 1} + ) do + remote_ip = Enum.join(Tuple.to_list(remote_ip), ".") + with {:ok, record} <- CommunitySubscriber |> ORM.findby_delete(community_id: community_id, user_id: user_id) do + update_geo_info(community_id, user_id, remote_ip, :dec) Community |> ORM.find(record.community_id) end end + + defp update_geo_info(community_id, user_id, remote_ip, method) do + {:ok, user} = ORM.find(User, user_id) + + case get_user_geocity(user.geo_city, remote_ip) do + {:ok, user_geo_city} -> + update_community_geo(community_id, user_geo_city, method) + + {:error, _} -> + {:ok, "pass"} + end + end + + defp get_user_geocity(nil, remote_ip) do + case RadarSearch.locate_city(remote_ip) do + {:ok, city} -> {:ok, city} + {:error, _} -> {:error, "update_geo_info error"} + end + end + + defp get_user_geocity(geo_city, _remote_ip), do: {:ok, geo_city} + + defp update_community_geo(community_id, city, method) do + with {:ok, community} <- Community |> ORM.find(community_id) do + community_geo_data = community.geo_info |> Map.get("data") + + cur_city_info = community_geo_data |> Enum.find(fn g -> g["city"] == city end) + new_city_info = update_geo_value(cur_city_info, method) + + community_geo_data = + community_geo_data + |> Enum.reject(fn g -> g["city"] == city end) + |> Kernel.++([new_city_info]) + + community |> ORM.update(%{geo_info: %{data: community_geo_data}}) + end + end + + defp update_geo_value(geo_info, :inc) do + Map.merge(geo_info, %{"value" => geo_info["value"] + 1}) + end + + defp update_geo_value(geo_info, :dec) do + Map.merge(geo_info, %{"value" => max(geo_info["value"] - 1, 0)}) + end end diff --git a/lib/mastani_server/cms/delegates/community_sync.ex b/lib/mastani_server/cms/delegates/community_sync.ex new file mode 100644 index 000000000..0dd0ed711 --- /dev/null +++ b/lib/mastani_server/cms/delegates/community_sync.ex @@ -0,0 +1,87 @@ +defmodule MastaniServer.CMS.Delegate.CommunitySync do + @moduledoc """ + community curd + """ + import Ecto.Query, warn: false + import Helper.ErrorCode + # import ShortMaps + + alias Helper.ORM + + alias MastaniServer.CMS.{ + Community, + CommunityWiki, + CommunityCheatsheet + } + + @doc """ + get wiki + """ + def get_wiki(%Community{raw: raw}) do + with {:ok, community} <- ORM.find_by(Community, raw: raw), + {:ok, wiki} <- ORM.find_by(CommunityWiki, community_id: community.id) do + CommunityWiki |> ORM.read(wiki.id, inc: :views) + end + end + + @doc """ + get cheatsheet + """ + def get_cheatsheet(%Community{raw: raw}) do + with {:ok, community} <- ORM.find_by(Community, raw: raw), + {:ok, wiki} <- ORM.find_by(CommunityCheatsheet, community_id: community.id) do + CommunityCheatsheet |> ORM.read(wiki.id, inc: :views) + end + end + + @doc """ + sync wiki + """ + def sync_github_content(%Community{id: id}, :wiki, attrs) do + with {:ok, community} <- ORM.find(Community, id) do + attrs = Map.merge(attrs, %{community_id: community.id}) + + CommunityWiki |> ORM.upsert_by([community_id: community.id], attrs) + end + end + + @doc """ + sync cheatsheet + """ + def sync_github_content(%Community{id: id}, :cheatsheet, attrs) do + with {:ok, community} <- ORM.find(Community, id) do + attrs = Map.merge(attrs, %{community_id: community.id}) + + CommunityCheatsheet |> ORM.upsert_by([community_id: community.id], attrs) + end + end + + @doc """ + add contributor to exsit wiki contributors list + """ + def add_contributor(%CommunityWiki{id: id}, contributor_attrs) do + do_add_contributor(CommunityWiki, id, contributor_attrs) + end + + def add_contributor(%CommunityCheatsheet{id: id}, contributor_attrs) do + do_add_contributor(CommunityCheatsheet, id, contributor_attrs) + end + + defp do_add_contributor(queryable, id, contributor_attrs) do + with {:ok, content} <- ORM.find(queryable, id) do + cur_contributors = + Enum.reduce(content.contributors, [], fn user, acc -> + acc ++ [Map.from_struct(user)] + end) + + case cur_contributors |> Enum.any?(&(&1.github_id == contributor_attrs.github_id)) do + true -> + {:error, [message: "already added", code: ecode(:already_exsit)]} + + false -> + new_contributors = %{contributors: cur_contributors ++ [contributor_attrs]} + content |> ORM.update(new_contributors) + end + end + end +end diff --git a/lib/mastani_server/cms/delegates/favorited_category.ex b/lib/mastani_server/cms/delegates/favorited_category.ex new file mode 100644 index 000000000..9b681011d --- /dev/null +++ b/lib/mastani_server/cms/delegates/favorited_category.ex @@ -0,0 +1,44 @@ +defmodule MastaniServer.CMS.Delegate.FavoritedContents do + @moduledoc """ + CURD operation on post/job/video ... + """ + alias Helper.ORM + + import Ecto.Query, warn: false + alias MastaniServer.Accounts.User + alias MastaniServer.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(:video, id, %User{id: user_id}) do + CMS.VideoFavorite + |> ORM.find_by(video_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/mastani_server/cms/delegates/passport_curd.ex b/lib/mastani_server/cms/delegates/passport_curd.ex index 8f5b0d78d..64ddb9603 100644 --- a/lib/mastani_server/cms/delegates/passport_curd.ex +++ b/lib/mastani_server/cms/delegates/passport_curd.ex @@ -25,8 +25,14 @@ defmodule MastaniServer.CMS.Delegate.PassportCURD do return a user's passport in CMS context """ def get_passport(%Accounts.User{} = user) do - with {:ok, passport} <- ORM.find_by(UserPasport, user_id: user.id) do - {:ok, passport.rules} + with {:ok, _} <- ORM.find(Accounts.User, user.id) do + case ORM.find_by(UserPasport, user_id: user.id) do + {:ok, passport} -> + {:ok, passport.rules} + + {:error, error} -> + {:ok, %{}} + end end end diff --git a/lib/mastani_server/cms/github_contributor.ex b/lib/mastani_server/cms/github_contributor.ex new file mode 100644 index 000000000..0fffff054 --- /dev/null +++ b/lib/mastani_server/cms/github_contributor.ex @@ -0,0 +1,28 @@ +defmodule MastaniServer.CMS.GithubContributor do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + + @required_fields ~w(github_id avatar nickname html_url)a + @optional_fields ~w(bio company location)a + + @type t :: %GithubContributor{} + embedded_schema do + field(:github_id, :string) + field(:avatar, :string) + field(:nickname, :string) + field(:bio, :string) + field(:company, :string) + field(:location, :string) + field(:html_url, :string) + end + + @doc false + def changeset(%GithubContributor{} = github_contributor, attrs) do + github_contributor + |> cast(attrs, @required_fields ++ @optional_fields) + |> validate_required(@required_fields) + end +end diff --git a/lib/mastani_server/cms/job.ex b/lib/mastani_server/cms/job.ex index 5cf259341..117d312a8 100644 --- a/lib/mastani_server/cms/job.ex +++ b/lib/mastani_server/cms/job.ex @@ -4,33 +4,40 @@ defmodule MastaniServer.CMS.Job do use Ecto.Schema import Ecto.Changeset - alias MastaniServer.CMS.{Author, Community, JobComment, JobFavorite, JobCommunityFlag, Tag} - @required_fields ~w(title company company_logo location body digest length)a - @optional_fields ~w(link_addr link_source min_education)a + alias MastaniServer.CMS.{ + Author, + Community, + JobComment, + JobFavorite, + JobStar, + JobViewer, + JobCommunityFlag, + Tag + } + + @required_fields ~w(title company company_logo body digest length)a + @optional_fields ~w(desc company_link link_addr copy_right salary exp education field finance scale)a @type t :: %Job{} schema "cms_jobs" do field(:title, :string) field(:company, :string) - field(:bonus, :string) field(:company_logo, :string) - field(:location, :string) + field(:company_link, :string) field(:desc, :string) field(:body, :string) belongs_to(:author, Author) field(:views, :integer, default: 0) field(:link_addr, :string) - field(:link_source, :string) - - field(:min_salary, :integer, default: 0) - field(:max_salary, :integer, default: 10_000_000) - - field(:min_experience, :integer, default: 1) - field(:max_experience, :integer, default: 3) + field(:copy_right, :string) - # college - bachelor - master - doctor - field(:min_education, :string) + field(:salary, :string) + field(:exp, :string) + field(:education, :string) + field(:field, :string) + field(:finance, :string) + field(:scale, :string) field(:digest, :string) field(:length, :integer) @@ -43,7 +50,8 @@ defmodule MastaniServer.CMS.Job do has_many(:comments, {"jobs_comments", JobComment}) has_many(:favorites, {"jobs_favorites", JobFavorite}) - # has_many(:stars, {"posts_stars", PostStar}) + has_many(:stars, {"jobs_stars", JobStar}) + has_many(:viewers, {"jobs_viewers", JobViewer}) many_to_many( :tags, diff --git a/lib/mastani_server/cms/job_comment.ex b/lib/mastani_server/cms/job_comment.ex index 3e4b80ca8..b2633c936 100644 --- a/lib/mastani_server/cms/job_comment.ex +++ b/lib/mastani_server/cms/job_comment.ex @@ -6,7 +6,7 @@ defmodule MastaniServer.CMS.JobComment do import Ecto.Changeset alias MastaniServer.Accounts - alias MastaniServer.CMS.{Job, JobCommentReply} + alias MastaniServer.CMS.{Job, JobCommentReply, JobCommentLike, JobCommentDislike} @required_fields ~w(body author_id job_id floor)a @optional_fields ~w(reply_id)a @@ -18,8 +18,11 @@ defmodule MastaniServer.CMS.JobComment do belongs_to(:author, Accounts.User, foreign_key: :author_id) belongs_to(:job, Job, foreign_key: :job_id) belongs_to(:reply_to, JobComment, foreign_key: :reply_id) - # belongs_to(:reply_to, JobComment, foreign_key: :job_id) + has_many(:replies, {"jobs_comments_replies", JobCommentReply}) + has_many(:likes, {"jobs_comments_likes", JobCommentLike}) + has_many(:dislikes, {"jobs_comments_dislikes", JobCommentDislike}) + timestamps(type: :utc_datetime) end diff --git a/lib/mastani_server/cms/job_comment_dislike.ex b/lib/mastani_server/cms/job_comment_dislike.ex new file mode 100644 index 000000000..e3d005532 --- /dev/null +++ b/lib/mastani_server/cms/job_comment_dislike.ex @@ -0,0 +1,29 @@ +defmodule MastaniServer.CMS.JobCommentDislike do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.Accounts + alias MastaniServer.CMS.JobComment + + @required_fields ~w(job_comment_id user_id)a + + @type t :: %JobCommentDislike{} + schema "jobs_comments_dislikes" do + belongs_to(:user, Accounts.User, foreign_key: :user_id) + belongs_to(:job_comment, JobComment, foreign_key: :job_comment_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%JobCommentDislike{} = job_comment_dislike, attrs) do + job_comment_dislike + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + |> foreign_key_constraint(:job_comment_id) + |> foreign_key_constraint(:user_id) + |> unique_constraint(:user_id, name: :jobs_comments_dislikes_user_id_job_comment_id_index) + end +end diff --git a/lib/mastani_server/cms/job_comment_like.ex b/lib/mastani_server/cms/job_comment_like.ex new file mode 100644 index 000000000..626c84a5c --- /dev/null +++ b/lib/mastani_server/cms/job_comment_like.ex @@ -0,0 +1,29 @@ +defmodule MastaniServer.CMS.JobCommentLike do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.Accounts + alias MastaniServer.CMS.JobComment + + @required_fields ~w(job_comment_id user_id)a + + @type t :: %JobCommentLike{} + schema "jobs_comments_likes" do + belongs_to(:user, Accounts.User, foreign_key: :user_id) + belongs_to(:job_comment, JobComment, foreign_key: :job_comment_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%JobCommentLike{} = job_comment_like, attrs) do + job_comment_like + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + |> foreign_key_constraint(:job_comment_id) + |> foreign_key_constraint(:user_id) + |> unique_constraint(:user_id, name: :jobs_comments_likes_user_id_job_comment_id_index) + end +end diff --git a/lib/mastani_server/cms/job_community_flag.ex b/lib/mastani_server/cms/job_community_flag.ex index 4919c4a85..b7cd9db97 100644 --- a/lib/mastani_server/cms/job_community_flag.ex +++ b/lib/mastani_server/cms/job_community_flag.ex @@ -7,7 +7,7 @@ defmodule MastaniServer.CMS.JobCommunityFlag do alias MastaniServer.CMS.{Community, Job} @required_fields ~w(job_id community_id)a - @optional_fields ~w(pin trash)a + @optional_fields ~w(trash)a @type t :: %JobCommunityFlag{} @@ -15,7 +15,6 @@ defmodule MastaniServer.CMS.JobCommunityFlag do belongs_to(:job, Job, foreign_key: :job_id) belongs_to(:community, Community, foreign_key: :community_id) - field(:pin, :boolean) field(:trash, :boolean) timestamps(type: :utc_datetime) diff --git a/lib/mastani_server/cms/job_favorite.ex b/lib/mastani_server/cms/job_favorite.ex index d12581360..d3e9862ac 100644 --- a/lib/mastani_server/cms/job_favorite.ex +++ b/lib/mastani_server/cms/job_favorite.ex @@ -8,19 +8,22 @@ defmodule MastaniServer.CMS.JobFavorite do alias MastaniServer.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, @required_fields) + |> cast(attrs, @optional_fields ++ @required_fields) |> validate_required(@required_fields) |> unique_constraint(:user_id, name: :jobs_favorites_user_id_job_id_index) end diff --git a/lib/mastani_server/cms/job_viewer.ex b/lib/mastani_server/cms/job_viewer.ex new file mode 100644 index 000000000..f922767e3 --- /dev/null +++ b/lib/mastani_server/cms/job_viewer.ex @@ -0,0 +1,27 @@ +defmodule MastaniServer.CMS.JobViewer do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.Accounts + alias MastaniServer.CMS.Job + + @required_fields ~w(job_id user_id)a + + @type t :: %JobViewer{} + schema "jobs_viewers" do + belongs_to(:job, Job, foreign_key: :job_id) + belongs_to(:user, Accounts.User, foreign_key: :user_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%JobViewer{} = job_viewer, attrs) do + job_viewer + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + |> unique_constraint(:user_id, name: :jobs_viewers_job_id_user_id_index) + end +end diff --git a/lib/mastani_server/cms/pined_job.ex b/lib/mastani_server/cms/pined_job.ex new file mode 100644 index 000000000..8495ae3b3 --- /dev/null +++ b/lib/mastani_server/cms/pined_job.ex @@ -0,0 +1,28 @@ +defmodule MastaniServer.CMS.PinedJob do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.CMS.{Community, Job} + + @required_fields ~w(job_id community_id)a + + @type t :: %PinedJob{} + schema "pined_jobs" do + belongs_to(:job, Job, foreign_key: :job_id) + belongs_to(:community, Community, foreign_key: :community_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%PinedJob{} = pined_job, attrs) do + pined_job + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + |> foreign_key_constraint(:job_id) + |> foreign_key_constraint(:community_id) + |> unique_constraint(:pined_jobs, name: :pined_jobs_job_id_community_id_index) + end +end diff --git a/lib/mastani_server/cms/pined_post.ex b/lib/mastani_server/cms/pined_post.ex new file mode 100644 index 000000000..fdc0d8a37 --- /dev/null +++ b/lib/mastani_server/cms/pined_post.ex @@ -0,0 +1,30 @@ +defmodule MastaniServer.CMS.PinedPost do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.CMS.{Community, Post, Topic} + + @required_fields ~w(post_id community_id)a + @optional_fields ~w(topic_id)a + + @type t :: %PinedPost{} + schema "pined_posts" do + belongs_to(:post, Post, foreign_key: :post_id) + belongs_to(:community, Community, foreign_key: :community_id) + belongs_to(:topic, Topic, foreign_key: :topic_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%PinedPost{} = pined_post, attrs) do + pined_post + |> cast(attrs, @optional_fields ++ @required_fields) + |> validate_required(@required_fields) + |> foreign_key_constraint(:post_id) + |> foreign_key_constraint(:community_id) + |> unique_constraint(:pined_posts, name: :pined_posts_post_id_community_id_topic_id_index) + end +end diff --git a/lib/mastani_server/cms/pined_repo.ex b/lib/mastani_server/cms/pined_repo.ex new file mode 100644 index 000000000..9d7465b01 --- /dev/null +++ b/lib/mastani_server/cms/pined_repo.ex @@ -0,0 +1,28 @@ +defmodule MastaniServer.CMS.PinedRepo do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.CMS.{Community, Repo} + + @required_fields ~w(repo_id community_id)a + + @type t :: %PinedRepo{} + schema "pined_repos" do + belongs_to(:repo, Repo, foreign_key: :repo_id) + belongs_to(:community, Community, foreign_key: :community_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%PinedRepo{} = pined_repo, attrs) do + pined_repo + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + |> foreign_key_constraint(:repo_id) + |> foreign_key_constraint(:community_id) + |> unique_constraint(:pined_repos, name: :pined_repos_repo_id_community_id_index) + end +end diff --git a/lib/mastani_server/cms/pined_video.ex b/lib/mastani_server/cms/pined_video.ex new file mode 100644 index 000000000..a4693448a --- /dev/null +++ b/lib/mastani_server/cms/pined_video.ex @@ -0,0 +1,28 @@ +defmodule MastaniServer.CMS.PinedVideo do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.CMS.{Community, Video} + + @required_fields ~w(video_id community_id)a + + @type t :: %PinedVideo{} + schema "pined_videos" do + belongs_to(:video, Video, foreign_key: :video_id) + belongs_to(:community, Community, foreign_key: :community_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%PinedVideo{} = pined_video, attrs) do + pined_video + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + |> foreign_key_constraint(:video_id) + |> foreign_key_constraint(:community_id) + |> unique_constraint(:pined_videos, name: :pined_videos_video_id_community_id_index) + end +end diff --git a/lib/mastani_server/cms/post.ex b/lib/mastani_server/cms/post.ex index c07bbf640..120500778 100644 --- a/lib/mastani_server/cms/post.ex +++ b/lib/mastani_server/cms/post.ex @@ -12,11 +12,13 @@ defmodule MastaniServer.CMS.Post do PostCommunityFlag, PostFavorite, PostStar, - Tag + PostViewer, + Tag, + Topic } @required_fields ~w(title body digest length)a - @optional_fields ~w(link_addr)a + @optional_fields ~w(link_addr copy_right)a @type t :: %Post{} schema "cms_posts" do @@ -24,6 +26,7 @@ defmodule MastaniServer.CMS.Post do field(:title, :string) field(:digest, :string) field(:link_addr, :string) + field(:copy_right, :string) field(:length, :integer) field(:views, :integer, default: 0) @@ -41,6 +44,7 @@ defmodule MastaniServer.CMS.Post do has_many(:comments, {"posts_comments", PostComment}) has_many(:favorites, {"posts_favorites", PostFavorite}) has_many(:stars, {"posts_stars", PostStar}) + has_many(:viewers, {"posts_viewers", PostViewer}) # The keys are inflected from the schema names! # see https://hexdocs.pm/ecto/Ecto.Schema.html many_to_many( @@ -53,6 +57,16 @@ defmodule MastaniServer.CMS.Post do on_replace: :delete ) + many_to_many( + :topics, + Topic, + join_through: "posts_topics", + join_keys: [post_id: :id, topic_id: :id], + # :delete_all will only remove data from the join source + on_delete: :delete_all, + on_replace: :delete + ) + many_to_many( :communities, Community, diff --git a/lib/mastani_server/cms/post_community_flag.ex b/lib/mastani_server/cms/post_community_flag.ex index a8aadd99c..47af39b72 100644 --- a/lib/mastani_server/cms/post_community_flag.ex +++ b/lib/mastani_server/cms/post_community_flag.ex @@ -7,7 +7,7 @@ defmodule MastaniServer.CMS.PostCommunityFlag do alias MastaniServer.CMS.{Community, Post} @required_fields ~w(post_id community_id)a - @optional_fields ~w(pin trash refined)a + @optional_fields ~w(trash)a @type t :: %PostCommunityFlag{} @@ -15,8 +15,6 @@ defmodule MastaniServer.CMS.PostCommunityFlag do belongs_to(:post, Post, foreign_key: :post_id) belongs_to(:community, Community, foreign_key: :community_id) - field(:pin, :boolean) - field(:refined, :boolean) field(:trash, :boolean) timestamps(type: :utc_datetime) diff --git a/lib/mastani_server/cms/post_favorite.ex b/lib/mastani_server/cms/post_favorite.ex index ae9423442..0ab14843a 100644 --- a/lib/mastani_server/cms/post_favorite.ex +++ b/lib/mastani_server/cms/post_favorite.ex @@ -14,7 +14,7 @@ defmodule MastaniServer.CMS.PostFavorite do schema "posts_favorites" do belongs_to(:user, Accounts.User, foreign_key: :user_id) belongs_to(:post, Post, foreign_key: :post_id) - # has_many(:category, UserFavoriteCategory) + belongs_to(:category, Accounts.FavoriteCategory) timestamps(type: :utc_datetime) diff --git a/lib/mastani_server/cms/post_viewer.ex b/lib/mastani_server/cms/post_viewer.ex new file mode 100644 index 000000000..c7fd396d2 --- /dev/null +++ b/lib/mastani_server/cms/post_viewer.ex @@ -0,0 +1,27 @@ +defmodule MastaniServer.CMS.PostViewer do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.Accounts + alias MastaniServer.CMS.Post + + @required_fields ~w(post_id user_id)a + + @type t :: %PostViewer{} + schema "posts_viewers" do + belongs_to(:post, Post, foreign_key: :post_id) + belongs_to(:user, Accounts.User, foreign_key: :user_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%PostViewer{} = post_viewer, attrs) do + post_viewer + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + |> unique_constraint(:user_id, name: :posts_viewers_post_id_user_id_index) + end +end diff --git a/lib/mastani_server/cms/repo.ex b/lib/mastani_server/cms/repo.ex index cc7df3010..327b1b169 100644 --- a/lib/mastani_server/cms/repo.ex +++ b/lib/mastani_server/cms/repo.ex @@ -4,38 +4,58 @@ defmodule MastaniServer.CMS.Repo do use Ecto.Schema import Ecto.Changeset - alias MastaniServer.CMS.{Author, Community, RepoBuilder, RepoCommunityFlag, Tag} - @required_fields ~w(repo_name desc readme language producer producer_link repo_link repo_star_count repo_fork_count repo_watch_count)a - @optional_fields ~w(views last_fetch_time) + alias MastaniServer.CMS.{ + Author, + Community, + RepoComment, + RepoContributor, + RepoFavorite, + RepoViewer, + RepoLang, + RepoCommunityFlag, + Tag + } + + @required_fields ~w(title owner_name owner_url repo_url desc readme star_count issues_count prs_count fork_count watch_count)a + @optional_fields ~w(last_sync homepage_url release_tag license) @type t :: %Repo{} schema "cms_repos" do - field(:repo_name, :string) + field(:title, :string) + field(:owner_name, :string) + field(:owner_url, :string) + field(:repo_url, :string) + field(:desc, :string) + field(:homepage_url, :string) field(:readme, :string) - field(:language, :string) - belongs_to(:author, Author) - field(:repo_link, :string) - field(:producer, :string) - field(:producer_link, :string) + field(:star_count, :integer) + field(:issues_count, :integer) + field(:prs_count, :integer) + field(:fork_count, :integer) + field(:watch_count, :integer) - field(:repo_star_count, :integer) - field(:repo_fork_count, :integer) - field(:repo_watch_count, :integer) + field(:license, :string) + field(:release_tag, :string) + embeds_one(:primary_language, RepoLang, on_replace: :delete) - field(:views, :integer, default: 0) + embeds_many(:contributors, RepoContributor, on_replace: :delete) + field(:views, :integer, default: 0) + belongs_to(:author, Author) has_many(:community_flags, {"repos_communities_flags", RepoCommunityFlag}) # NOTE: this one is tricky, pin is dynamic changed when return by func: add_pin_contents_ifneed field(:pin, :boolean, default_value: false) field(:trash, :boolean, default_value: false) - field(:last_fetch_time, :utc_datetime) - # TODO: replace RepoBuilder with paged user map - has_many(:builders, {"repos_builders", RepoBuilder}) + field(:last_sync, :utc_datetime) + + has_many(:comments, {"repos_comments", RepoComment}) + has_many(:favorites, {"repos_favorites", RepoFavorite}) + has_many(:viewers, {"repos_viewers", RepoViewer}) many_to_many( :tags, @@ -60,9 +80,8 @@ defmodule MastaniServer.CMS.Repo do def changeset(%Repo{} = repo, attrs) do repo |> cast(attrs, @optional_fields ++ @required_fields) + |> cast_embed(:contributors, with: &RepoContributor.changeset/2) + |> cast_embed(:primary_language, with: &RepoLang.changeset/2) |> validate_required(@required_fields) - - # |> foreign_key_constraint(:posts_tags, name: :posts_tags_tag_id_fkey) - # |> foreign_key_constraint(name: :posts_tags_tag_id_fkey) end end diff --git a/lib/mastani_server/cms/repo_builder.ex b/lib/mastani_server/cms/repo_builder.ex deleted file mode 100644 index dcc3fb4a7..000000000 --- a/lib/mastani_server/cms/repo_builder.ex +++ /dev/null @@ -1,26 +0,0 @@ -defmodule MastaniServer.CMS.RepoBuilder do - @moduledoc false - alias __MODULE__ - - use Ecto.Schema - import Ecto.Changeset - - @required_fields ~w(nickname avatar link)a - @optional_fields ~w(bio) - - @type t :: %RepoBuilder{} - schema "cms_repo_users" do - field(:nickname, :string) - field(:avatar, :string) - field(:link, :string) - - timestamps(type: :utc_datetime) - end - - @doc false - def changeset(%RepoBuilder{} = repo_builder, attrs) do - repo_builder - |> cast(attrs, @optional_fields ++ @required_fields) - |> validate_required(@required_fields) - end -end diff --git a/lib/mastani_server/cms/repo_comment.ex b/lib/mastani_server/cms/repo_comment.ex new file mode 100644 index 000000000..13ea5dbb9 --- /dev/null +++ b/lib/mastani_server/cms/repo_comment.ex @@ -0,0 +1,43 @@ +defmodule MastaniServer.CMS.RepoComment do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.Accounts + + alias MastaniServer.CMS.{ + Repo, + RepoCommentDislike, + RepoCommentLike, + RepoCommentReply + } + + @required_fields ~w(body author_id repo_id floor)a + @optional_fields ~w(reply_id)a + + @type t :: %RepoComment{} + schema "repos_comments" do + field(:body, :string) + field(:floor, :integer) + belongs_to(:author, Accounts.User, foreign_key: :author_id) + belongs_to(:repo, Repo, foreign_key: :repo_id) + belongs_to(:reply_to, RepoComment, foreign_key: :reply_id) + + has_many(:replies, {"repos_comments_replies", RepoCommentReply}) + has_many(:likes, {"repos_comments_likes", RepoCommentLike}) + has_many(:dislikes, {"repos_comments_dislikes", RepoCommentDislike}) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%RepoComment{} = repo_comment, attrs) do + repo_comment + |> cast(attrs, @required_fields ++ @optional_fields) + |> validate_required(@required_fields) + |> validate_length(:body, min: 1) + |> foreign_key_constraint(:repo_id) + |> foreign_key_constraint(:author_id) + end +end diff --git a/lib/mastani_server/cms/repo_comment_dislike.ex b/lib/mastani_server/cms/repo_comment_dislike.ex new file mode 100644 index 000000000..bcaa2e5a0 --- /dev/null +++ b/lib/mastani_server/cms/repo_comment_dislike.ex @@ -0,0 +1,29 @@ +defmodule MastaniServer.CMS.RepoCommentDislike do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.Accounts + alias MastaniServer.CMS.RepoComment + + @required_fields ~w(repo_comment_id user_id)a + + @type t :: %RepoCommentDislike{} + schema "repos_comments_dislikes" do + belongs_to(:user, Accounts.User, foreign_key: :user_id) + belongs_to(:repo_comment, RepoComment, foreign_key: :repo_comment_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%RepoCommentDislike{} = repo_comment_dislike, attrs) do + repo_comment_dislike + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + |> foreign_key_constraint(:repo_comment_id) + |> foreign_key_constraint(:user_id) + |> unique_constraint(:user_id, name: :repos_comments_dislikes_user_id_repo_comment_id_index) + end +end diff --git a/lib/mastani_server/cms/repo_comment_like.ex b/lib/mastani_server/cms/repo_comment_like.ex new file mode 100644 index 000000000..6cf25a43a --- /dev/null +++ b/lib/mastani_server/cms/repo_comment_like.ex @@ -0,0 +1,29 @@ +defmodule MastaniServer.CMS.RepoCommentLike do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.Accounts + alias MastaniServer.CMS.RepoCommentLike + + @required_fields ~w(repo_comment_id user_id)a + + @type t :: %RepoCommentLike{} + schema "repos_comments_likes" do + belongs_to(:user, Accounts.User, foreign_key: :user_id) + belongs_to(:repo_comment, RepoCommentLike, foreign_key: :repo_comment_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%RepoCommentLike{} = repo_comment_like, attrs) do + repo_comment_like + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + |> foreign_key_constraint(:repo_comment_id) + |> foreign_key_constraint(:user_id) + |> unique_constraint(:user_id, name: :repos_comments_likes_user_id_repo_comment_id_index) + end +end diff --git a/lib/mastani_server/cms/repo_comment_reply.ex b/lib/mastani_server/cms/repo_comment_reply.ex new file mode 100644 index 000000000..d9ff580a1 --- /dev/null +++ b/lib/mastani_server/cms/repo_comment_reply.ex @@ -0,0 +1,27 @@ +defmodule MastaniServer.CMS.RepoCommentReply do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.CMS.RepoComment + + @required_fields ~w(repo_comment_id reply_id)a + + @type t :: %RepoCommentReply{} + schema "repos_comments_replies" do + belongs_to(:repo_comment, RepoComment, foreign_key: :repo_comment_id) + belongs_to(:reply, RepoComment, foreign_key: :reply_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%RepoCommentReply{} = repo_comment_reply, attrs) do + repo_comment_reply + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + |> foreign_key_constraint(:repo_comment_id) + |> foreign_key_constraint(:reply_id) + end +end diff --git a/lib/mastani_server/cms/repo_community_flag.ex b/lib/mastani_server/cms/repo_community_flag.ex index d447ad9a8..4c5cbe7aa 100644 --- a/lib/mastani_server/cms/repo_community_flag.ex +++ b/lib/mastani_server/cms/repo_community_flag.ex @@ -7,7 +7,7 @@ defmodule MastaniServer.CMS.RepoCommunityFlag do alias MastaniServer.CMS.{Community, Repo} @required_fields ~w(repo_id community_id)a - @optional_fields ~w(pin trash)a + @optional_fields ~w(trash)a @type t :: %RepoCommunityFlag{} @@ -15,7 +15,6 @@ defmodule MastaniServer.CMS.RepoCommunityFlag do belongs_to(:repo, Repo, foreign_key: :repo_id) belongs_to(:community, Community, foreign_key: :community_id) - field(:pin, :boolean) field(:trash, :boolean) timestamps(type: :utc_datetime) diff --git a/lib/mastani_server/cms/repo_contributor.ex b/lib/mastani_server/cms/repo_contributor.ex new file mode 100644 index 000000000..ffac880c8 --- /dev/null +++ b/lib/mastani_server/cms/repo_contributor.ex @@ -0,0 +1,23 @@ +defmodule MastaniServer.CMS.RepoContributor do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + + @required_fields ~w(avatar nickname html_url)a + + @type t :: %RepoContributor{} + embedded_schema do + field(:avatar, :string) + field(:nickname, :string) + field(:html_url, :string) + end + + @doc false + def changeset(%RepoContributor{} = repo_contributor, attrs) do + repo_contributor + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + end +end diff --git a/lib/mastani_server/cms/repo_favorite.ex b/lib/mastani_server/cms/repo_favorite.ex new file mode 100644 index 000000000..fea48c8a3 --- /dev/null +++ b/lib/mastani_server/cms/repo_favorite.ex @@ -0,0 +1,30 @@ +defmodule MastaniServer.CMS.RepoFavorite do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.Accounts + alias MastaniServer.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 +end diff --git a/lib/mastani_server/cms/repo_lang.ex b/lib/mastani_server/cms/repo_lang.ex new file mode 100644 index 000000000..4df263f73 --- /dev/null +++ b/lib/mastani_server/cms/repo_lang.ex @@ -0,0 +1,22 @@ +defmodule MastaniServer.CMS.RepoLang do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + + @required_fields ~w(name color)a + + @type t :: %RepoLang{} + embedded_schema do + field(:name, :string) + field(:color, :string) + end + + @doc false + def changeset(%RepoLang{} = repo_lang, attrs) do + repo_lang + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + end +end diff --git a/lib/mastani_server/cms/repo_viewer.ex b/lib/mastani_server/cms/repo_viewer.ex new file mode 100644 index 000000000..47597bb37 --- /dev/null +++ b/lib/mastani_server/cms/repo_viewer.ex @@ -0,0 +1,27 @@ +defmodule MastaniServer.CMS.RepoViewer do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.Accounts + alias MastaniServer.CMS.Repo + + @required_fields ~w(repo_id user_id)a + + @type t :: %RepoViewer{} + schema "repos_viewers" do + belongs_to(:repo, Repo, foreign_key: :repo_id) + belongs_to(:user, Accounts.User, foreign_key: :user_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%RepoViewer{} = repo_viewer, attrs) do + repo_viewer + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + |> unique_constraint(:user_id, name: :repos_viewers_repo_id_user_id_index) + end +end diff --git a/lib/mastani_server/cms/tag.ex b/lib/mastani_server/cms/tag.ex index 2e0dcb64a..295a8ec52 100644 --- a/lib/mastani_server/cms/tag.ex +++ b/lib/mastani_server/cms/tag.ex @@ -4,9 +4,10 @@ defmodule MastaniServer.CMS.Tag do use Ecto.Schema import Ecto.Changeset - alias MastaniServer.CMS.{Author, Community, Job, Post, Video} + alias MastaniServer.CMS.{Author, Community, Topic, Job, Post, Video} - @required_fields ~w(thread title color author_id community_id)a + @required_fields ~w(thread title color author_id topic_id community_id)a + # @required_fields ~w(thread title color author_id community_id)a @type t :: %Tag{} schema "tags" do @@ -14,6 +15,7 @@ defmodule MastaniServer.CMS.Tag do field(:color, :string) field(:thread, :string) belongs_to(:community, Community) + belongs_to(:topic, Topic) belongs_to(:author, Author) many_to_many( @@ -49,7 +51,7 @@ defmodule MastaniServer.CMS.Tag do |> validate_required(@required_fields) |> foreign_key_constraint(:user_id) |> foreign_key_constraint(:community_id) - |> unique_constraint(:tag_duplicate, name: :tags_community_id_thread_title_index) + |> unique_constraint(:tag_duplicate, name: :tags_community_id_thread_topic_id_title_index) # |> foreign_key_constraint(name: :posts_tags_tag_id_fkey) end diff --git a/lib/mastani_server/cms/topic.ex b/lib/mastani_server/cms/topic.ex new file mode 100644 index 000000000..4050eb7b0 --- /dev/null +++ b/lib/mastani_server/cms/topic.ex @@ -0,0 +1,49 @@ +defmodule MastaniServer.CMS.Topic do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + # alias MastaniServer.CMS.{Author, Community, Job, Post, Video} + + @required_fields ~w(thread title raw)a + + @type t :: %Topic{} + schema "topics" do + field(:title, :string) + field(:thread, :string) + field(:raw, :string) + + # many_to_many( + # :posts, + # Post, + # join_through: "posts_tags", + # join_keys: [post_id: :id, tag_id: :id], + # on_delete: :delete_all + # ) + + # many_to_many( + # :videos, + # Video, + # join_through: "videos_tags", + # join_keys: [video_id: :id, tag_id: :id] + # ) + + # many_to_many( + # :jobs, + # Job, + # join_through: "jobs_tags", + # join_keys: [job_id: :id, tag_id: :id] + # ) + + timestamps(type: :utc_datetime) + end + + def changeset(%Topic{} = topic, attrs) do + topic + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + + # |> unique_constraint(:tag_duplicate, name: :tags_community_id_thread_title_index) + end +end diff --git a/lib/mastani_server/cms/utils/loader.ex b/lib/mastani_server/cms/utils/loader.ex index 1114404e4..9d868efc2 100644 --- a/lib/mastani_server/cms/utils/loader.ex +++ b/lib/mastani_server/cms/utils/loader.ex @@ -7,35 +7,77 @@ defmodule MastaniServer.CMS.Utils.Loader do alias Helper.QueryBuilder alias MastaniServer.Repo # alias MastaniServer.Accounts + alias MastaniServer.CMS.Repo, as: CMSRepo + alias MastaniServer.CMS.{ Author, CommunityEditor, CommunitySubscriber, CommunityThread, - JobCommentReply, + # POST Post, + PostViewer, PostComment, PostCommentDislike, PostCommentLike, PostCommentReply, PostFavorite, - PostStar - # job comment - # JobComment, + PostStar, + # JOB + Job, + JobViewer, + JobFavorite, + # JobStar, + JobComment, + JobCommentReply, + JobCommentDislike, + JobCommentLike, + # Video + Video, + VideoViewer, + VideoFavorite, + VideoStar, + VideoComment, + VideoCommentReply, + VideoCommentDislike, + VideoCommentLike, + # Repo, + RepoViewer, + RepoFavorite, + RepoComment, + RepoCommentReply, + RepoCommentLike, + RepoCommentDislike } def data, do: Dataloader.Ecto.new(Repo, query: &query/2, run_batch: &run_batch/5) # Big thanks: https://elixirforum.com/t/grouping-error-in-absinthe-dadaloader/13671/2 # see also: https://github.com/absinthe-graphql/dataloader/issues/25 - def run_batch(Post, post_query, :posts_count, community_ids, repo_opts) do + def run_batch(Post, content_query, :posts_count, community_ids, repo_opts) do + query_content_counts(content_query, community_ids, repo_opts) + end + + def run_batch(Job, content_query, :jobs_count, community_ids, repo_opts) do + query_content_counts(content_query, community_ids, repo_opts) + end + + def run_batch(Video, content_query, :videos_count, community_ids, repo_opts) do + query_content_counts(content_query, community_ids, repo_opts) + end + + def run_batch(CMSRepo, content_query, :repos_count, community_ids, repo_opts) do + query_content_counts(content_query, community_ids, repo_opts) + end + + defp query_content_counts(content_query, community_ids, repo_opts) do query = from( - p in post_query, - join: c in assoc(p, :communities), + content in content_query, + join: c in assoc(content, :communities), where: c.id in ^community_ids, group_by: c.id, - select: {c.id, [count(p.id)]} + select: {c.id, [count(content.id)]} ) results = @@ -62,101 +104,7 @@ defmodule MastaniServer.CMS.Utils.Loader do for id <- post_ids, do: Map.get(results, id, [0]) end - def run_batch(PostComment, comment_query, :cp_users, post_ids, repo_opts) do - # IO.inspect(comment_query, label: "# run_batch # comment_query") - - sq = - from( - pc in comment_query, - join: a in assoc(pc, :author), - select: %{id: a.id, row_number: fragment("row_number() OVER (PARTITION BY author_id)")} - ) - - query = - from( - pc in comment_query, - join: s in subquery(sq), - on: s.id == pc.author_id, - where: s.row_number == 10, - select: {pc.post_id, s.id} - ) - - # query = comment_query - # |> join(:inner, [c], a in assoc(c, :author)) - # |> distinct([c, a], c.post_id) - # |> join(:inner_lateral, [c, a], u in fragment("SELECT * FROM users AS us WHERE us.id = ? LIMIT 1", a.id)) - # |> join(:inner_lateral, [c, a], u in fragment("SELECT * FROM users AS us WHERE us.id > ? LIMIT 1", 100)) - # |> select([c, a, u], {c.post_id, u.id, u.nickname}) - - results = - query - # |> IO.inspect(label: "before") - |> Repo.all(repo_opts) - # |> IO.inspect(label: "geting fuck") - |> bat_man() - - # results = - # comment_query - # |> join(:inner, [c], a in assoc(c, :author)) - # |> group_by([c, a], a.id) - # |> group_by([c, a], c.post_id) - # |> select([c, a], {c.post_id, a}) - # --------- - # |> join(:inner, [c], s in subquery(sq), on: s.id == c.post_id) - # |> join(:inner, [c], a in subquery(isubquery), c.post_id == 106) - # |> join(:inner_lateral, [c], a in fragment("SELECT * FROM users AS u WHERE u.id = ? LIMIT 3", c.post_id)) - # |> join(:inner_lateral, [c], a in fragment("SELECT * FROM users WHERE users.id > ? LIMIT 3", 100)) - # |> join(:inner_lateral, [c], a in fragment("SELECT * FROM posts_comments JOIN users ON users.id = ? LIMIT 2", c.author_id)) - # |> join(:inner_lateral, [c], a in fragment("SELECT * FROM posts_comments AS pc WHERE pc.author_id = ? LIMIT 2", 185)) - # |> join(:inner_lateral, [c], a in fragment("SELECT ROW_NUMBER() OVER (PARTITION BY ?) FROM posts_comments AS pc GROUP BY pc.post_id", c.post_id)) - # |> distinct([c, a], c.post_id) - # |> join(:inner_lateral, [c, a], x in fragment("SELECT * FROM posts_comments JOIN users ON users.id = posts_comments.author_id WHERE post_id = ? LIMIT 2", c.post_id)) - # |> join(:inner_lateral, [c, a], x in fragment("SELECT * FROM posts_comments JOIN users ON users.id = posts_comments.author_id LIMIT 3")) - # |> select([c,a,x], {c.post_id, x.author_id}) - # |> select([c,a,x], {c.post_id, a.id}) - # |> where([c, a], a.row_number < 3) - # |> join(:inner, [c], a in assoc(c, :author)) - # |> join(:inner, [c], a in subquery(isubquery)) - # |> group_by([c, a, x], x.author_id) - # |> distinct([c, a], a.author_id) - # |> select([c, a], {c.post_id, a.author_id}) - # |> select([c, a], {c.post_id, fragment("max(?) OVER (PARTITION BY ?)", a.id, a.id)}) - # |> select([c, a], %{post_id: c.post_id, user: fragment("max(?) OVER (PARTITION BY ?)", a.id, a.id)}) - # |> select([c, a], fragment("SELECT ROW_NUMBER() OVER (PARTITION BY ?) FROM cms_authors AS r , ", a.id)) - # |> join([c], c in subquery(sq), on: c.post_id == bq.id) - # |> having([c, a], count("*") < 10) - # |> having([c, a], a.id < 180) - # |> limit(3) - # |> order_by([p, s], desc: fragment("count(?)", s.id)) - # |> distinct([c, a], a.id) - # |> Repo.all(repo_opts) - # |> IO.inspect(label: "get fuck") - # |> bat_man() - - for id <- post_ids, do: Map.get(results, id, []) - end - - # TODO: use meta-programing to extract all query below - # -------------------- - def bat_man(data) do - # TODO refactor later - data - |> Enum.group_by(fn {x, _} -> x end) - |> Enum.map(fn {x, y} -> - {x, - Enum.reduce(y, [], fn kv, acc -> - {_, v} = kv - acc ++ [v] - end)} - end) - |> Map.new() - end - def query(Author, _args) do - # you cannot use preload with select together - # https://stackoverflow.com/questions/43010352/ecto-select-relations-from-preload - # see also - # https://github.com/elixir-ecto/ecto/issues/1145 from(a in Author, join: u in assoc(a, :user), select: u) end @@ -169,41 +117,20 @@ defmodule MastaniServer.CMS.Utils.Loader do ) end - @doc """ - get unique participators join in comments - """ - def query({"posts_comments", PostComment}, %{filter: filter, unique: true}) do - # def query({"posts_comments", PostComment}, %{unique: true}) do - PostComment - # |> QueryBuilder.members_pack(args) - |> QueryBuilder.filter_pack(filter) - |> join(:inner, [c], a in assoc(c, :author)) - |> distinct([c, a], a.id) - |> select([c, a], a) + def query({"posts_viewers", PostViewer}, %{cur_user: cur_user}) do + PostViewer |> where([pv], pv.user_id == ^cur_user.id) end - def query({"posts_comments", PostComment}, %{count: _, unique: true}) do - # TODO: not very familar with SQL, but it has to be 2 group_by to work, check later - # and the expect count should be the length of reault - PostComment - |> join(:inner, [c], a in assoc(c, :author)) - |> distinct([c, a], a.id) - |> group_by([c, a], a.id) - |> group_by([c, a], c.post_id) - |> select([c, a], count(c.id)) + def query({"jobs_viewers", JobViewer}, %{cur_user: cur_user}) do + JobViewer |> where([pv], pv.user_id == ^cur_user.id) end - def query({"posts_comments", PostComment}, %{count: _}) do - PostComment - |> group_by([c], c.post_id) - |> select([c], count(c.id)) + def query({"videos_viewers", VideoViewer}, %{cur_user: cur_user}) do + VideoViewer |> where([pv], pv.user_id == ^cur_user.id) end - # def query({"posts_comments", PostComment}, %{filter: %{first: first}} = filter) do - def query({"posts_comments", PostComment}, %{filter: filter}) do - PostComment - # |> limit(3) - |> QueryBuilder.filter_pack(filter) + def query({"repos_viewers", RepoViewer}, %{cur_user: cur_user}) do + RepoViewer |> where([pv], pv.user_id == ^cur_user.id) end @doc """ @@ -220,6 +147,22 @@ defmodule MastaniServer.CMS.Utils.Loader 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({"videos_favorites", VideoFavorite}, args) do + VideoFavorite |> QueryBuilder.members_pack(args) + end + + def query({"videos_stars", VideoStar}, args) do + VideoStar |> QueryBuilder.members_pack(args) + end + def query({"communities_subscribers", CommunitySubscriber}, args) do CommunitySubscriber |> QueryBuilder.members_pack(args) end @@ -228,7 +171,41 @@ defmodule MastaniServer.CMS.Utils.Loader do CommunityEditor |> QueryBuilder.members_pack(args) end - # for comments replies, likes, repliesCount, likesCount... + # ------- post comments ------ + @doc """ + get unique participators join in comments + """ + # NOTE: this is NOT the right solution + # should use WINDOW function + # see https://github.com/coderplanets/coderplanets_server/issues/16 + def query({"posts_comments", PostComment}, %{filter: filter, unique: true}) do + PostComment + # |> QueryBuilder.filter_pack(filter) + |> join(:inner, [c], a in assoc(c, :author)) + |> distinct([c, a], a.id) + |> select([c, a], a) + end + + def query({"posts_comments", PostComment}, %{count: _, unique: true}) do + PostComment + |> join(:inner, [c], a in assoc(c, :author)) + |> distinct([c, a], a.id) + |> group_by([c, a], a.id) + |> group_by([c, a], c.post_id) + |> select([c, a], count(c.id)) + end + + def query({"posts_comments", PostComment}, %{count: _}) do + PostComment + |> group_by([c], c.post_id) + |> select([c], count(c.id)) + end + + def query({"posts_comments", PostComment}, %{filter: filter}) do + PostComment + |> QueryBuilder.filter_pack(filter) + end + def query({"posts_comments_replies", PostCommentReply}, %{count: _}) do PostCommentReply |> group_by([c], c.post_comment_id) @@ -278,6 +255,29 @@ defmodule MastaniServer.CMS.Utils.Loader do end # ---- job comments ------ + def query({"jobs_comments", JobComment}, %{filter: filter, unique: true}) do + JobComment + # |> QueryBuilder.filter_pack(filter) + |> join(:inner, [c], a in assoc(c, :author)) + |> distinct([c, a], a.id) + |> select([c, a], a) + end + + def query({"jobs_comments", JobComment}, %{count: _, unique: true}) do + JobComment + |> join(:inner, [c], a in assoc(c, :author)) + |> distinct([c, a], a.id) + |> group_by([c, a], a.id) + |> group_by([c, a], c.job_id) + |> select([c, a], count(c.id)) + end + + def query({"jobs_comments", JobComment}, %{count: _}) do + JobComment + |> group_by([c], c.job_id) + |> select([c], count(c.id)) + end + def query({"jobs_comments_replies", JobCommentReply}, %{count: _}) do JobCommentReply |> group_by([c], c.job_comment_id) @@ -295,12 +295,192 @@ defmodule MastaniServer.CMS.Utils.Loader do |> select([c, r], r) end - # ---- job ------ + def query({"jobs_comments_likes", JobCommentLike}, %{count: _}) do + JobCommentLike + |> group_by([f], f.job_comment_id) + |> select([f], count(f.id)) + end - # default loader - def query(queryable, _args) do - # IO.inspect(queryable, label: "default loader") - # IO.inspect(args, label: "default args") - queryable + def query({"jobs_comments_likes", JobCommentLike}, %{viewer_did: _, cur_user: cur_user}) do + JobCommentLike |> where([f], f.user_id == ^cur_user.id) + end + + def query({"jobs_comments_likes", JobCommentLike}, %{filter: _filter} = args) do + JobCommentLike |> QueryBuilder.members_pack(args) + end + + def query({"jobs_comments_dislikes", JobCommentDislike}, %{count: _}) do + JobCommentDislike + |> group_by([f], f.job_comment_id) + |> select([f], count(f.id)) + end + + def query({"jobs_comments_dislikes", JobCommentDislike}, %{ + viewer_did: _, + cur_user: cur_user + }) do + JobCommentDislike |> where([f], f.user_id == ^cur_user.id) + end + + def query({"jobs_comments_dislikes", JobCommentDislike}, %{filter: _filter} = args) do + JobCommentDislike |> QueryBuilder.members_pack(args) + end + + # ---- job comments end------ + + # ---- video comments ------ + def query({"videos_comments", VideoComment}, %{filter: filter, unique: true}) do + VideoComment + # |> QueryBuilder.filter_pack(filter) + |> join(:inner, [c], a in assoc(c, :author)) + |> distinct([c, a], a.id) + |> select([c, a], a) + end + + def query({"videos_comments", VideoComment}, %{count: _, unique: true}) do + VideoComment + |> join(:inner, [c], a in assoc(c, :author)) + |> distinct([c, a], a.id) + |> group_by([c, a], a.id) + |> group_by([c, a], c.video_id) + |> select([c, a], count(c.id)) + end + + def query({"videos_comments", VideoComment}, %{count: _}) do + VideoComment + |> group_by([c], c.video_id) + |> select([c], count(c.id)) + end + + def query({"videos_comments_replies", VideoCommentReply}, %{count: _}) do + VideoCommentReply + |> group_by([c], c.video_comment_id) + |> select([c], count(c.id)) + end + + def query({"videos_comments_replies", VideoCommentReply}, %{filter: filter}) do + VideoCommentReply |> QueryBuilder.load_inner_replies(filter) + end + + def query({"videos_comments_replies", VideoCommentReply}, %{reply_to: _}) do + VideoCommentReply + |> join(:inner, [c], r in assoc(c, :video_comment)) + |> select([c, r], r) + end + + def query({"videos_comments_likes", VideoCommentLike}, %{count: _}) do + VideoCommentLike + |> group_by([f], f.video_comment_id) + |> select([f], count(f.id)) + end + + def query({"videos_comments_likes", VideoCommentLike}, %{viewer_did: _, cur_user: cur_user}) do + VideoCommentLike |> where([f], f.user_id == ^cur_user.id) + end + + def query({"videos_comments_likes", VideoCommentLike}, %{filter: _filter} = args) do + VideoCommentLike + |> QueryBuilder.members_pack(args) + end + + def query({"videos_comments_dislikes", VideoCommentDislike}, %{count: _}) do + VideoCommentDislike + |> group_by([f], f.video_comment_id) + |> select([f], count(f.id)) + end + + # component dislikes + def query({"videos_comments_dislikes", VideoCommentDislike}, %{ + viewer_did: _, + cur_user: cur_user + }) do + VideoCommentDislike |> where([f], f.user_id == ^cur_user.id) end + + def query({"videos_comments_dislikes", VideoCommentDislike}, %{filter: _filter} = args) do + VideoCommentDislike + |> QueryBuilder.members_pack(args) + end + + # ---- video ------ + + # --- repo comments ------ + def query({"repos_comments", RepoComment}, %{filter: filter, unique: true}) do + RepoComment + # |> QueryBuilder.filter_pack(filter) + |> join(:inner, [c], a in assoc(c, :author)) + |> distinct([c, a], a.id) + |> select([c, a], a) + end + + def query({"repos_comments", RepoComment}, %{count: _, unique: true}) do + RepoComment + |> join(:inner, [c], a in assoc(c, :author)) + |> distinct([c, a], a.id) + |> group_by([c, a], a.id) + |> group_by([c, a], c.repo_id) + |> select([c, a], count(c.id)) + end + + def query({"repos_comments", RepoComment}, %{count: _}) do + RepoComment + |> group_by([c], c.repo_id) + |> select([c], count(c.id)) + end + + def query({"repos_comments_replies", RepoCommentReply}, %{count: _}) do + RepoCommentReply + |> group_by([c], c.repo_comment_id) + |> select([c], count(c.id)) + end + + def query({"repos_comments_replies", RepoCommentReply}, %{filter: filter}) do + RepoCommentReply + |> QueryBuilder.load_inner_replies(filter) + end + + def query({"repos_comments_replies", RepoCommentReply}, %{reply_to: _}) do + RepoCommentReply + |> join(:inner, [c], r in assoc(c, :repo_comment)) + |> select([c, r], r) + end + + def query({"repos_comments_likes", RepoCommentLike}, %{count: _}) do + RepoCommentLike + |> group_by([f], f.repo_comment_id) + |> select([f], count(f.id)) + end + + def query({"repos_comments_likes", RepoCommentLike}, %{viewer_did: _, cur_user: cur_user}) do + RepoCommentLike |> where([f], f.user_id == ^cur_user.id) + end + + def query({"repos_comments_likes", RepoCommentLike}, %{filter: _filter} = args) do + RepoCommentLike + |> QueryBuilder.members_pack(args) + end + + def query({"repos_comments_dislikes", RepoCommentDislike}, %{count: _}) do + RepoCommentDislike + |> group_by([f], f.repo_comment_id) + |> select([f], count(f.id)) + end + + # component dislikes + def query({"repos_comments_dislikes", RepoCommentDislike}, %{ + viewer_did: _, + cur_user: cur_user + }) do + RepoCommentDislike |> where([f], f.user_id == ^cur_user.id) + end + + def query({"repos_comments_dislikes", RepoCommentDislike}, %{filter: _filter} = args) do + RepoCommentDislike + |> QueryBuilder.members_pack(args) + end + + # --- repo ------ + + # default loader + def query(queryable, _args), do: queryable end diff --git a/lib/mastani_server/cms/utils/matcher.ex b/lib/mastani_server/cms/utils/matcher.ex index be3b7e186..9e2ad6643 100644 --- a/lib/mastani_server/cms/utils/matcher.ex +++ b/lib/mastani_server/cms/utils/matcher.ex @@ -6,20 +6,42 @@ defmodule MastaniServer.CMS.Utils.Matcher do alias MastaniServer.CMS.{ Community, + # threads Post, Video, Repo, Job, + # viewer + PostViewer, + JobViewer, + VideoViewer, + RepoViewer, + # reactions PostFavorite, JobFavorite, + VideoFavorite, + RepoFavorite, PostStar, JobStar, + VideoStar, + # comments PostComment, JobComment, - Tag, - Community, + VideoComment, + RepoComment, + # commtnes reaction PostCommentLike, PostCommentDislike, + JobCommentLike, + JobCommentDislike, + VideoCommentLike, + VideoCommentDislike, + RepoCommentLike, + RepoCommentDislike, + # + Tag, + Community, + # flags PostCommunityFlag, JobCommunityFlag, RepoCommunityFlag, @@ -43,7 +65,8 @@ defmodule MastaniServer.CMS.Utils.Matcher do ######################################### ## posts ... ######################################### - def match_action(:post, :self), do: {:ok, %{target: Post, reactor: Post, preload: :author}} + 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}} @@ -66,10 +89,14 @@ defmodule MastaniServer.CMS.Utils.Matcher do ######################################### ## jobs ... ######################################### - def match_action(:job, :self), do: {:ok, %{target: Job, reactor: Job, preload: :author}} + def match_action(:job, :self), + do: {:ok, %{target: Job, reactor: Job, preload: :author, viewer: JobViewer}} def match_action(:job, :community), - do: {:ok, %{target: Job, reactor: Community, flag: JobCommunityFlags}} + 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}} @@ -77,25 +104,58 @@ defmodule MastaniServer.CMS.Utils.Matcher do def match_action(:job, :comment), do: {:ok, %{target: Job, reactor: JobComment, preload: :author}} - def match_action(:job, :favorite), - do: {:ok, %{target: Job, reactor: JobFavorite, preload: :user}} + def match_action(:job_comment, :like), + do: {:ok, %{target: JobComment, reactor: JobCommentLike}} + + def match_action(:job_comment, :dislike), + do: {:ok, %{target: JobComment, reactor: JobCommentDislike}} ######################################### ## videos ... ######################################### - def match_action(:video, :self), do: {:ok, %{target: Video, reactor: Video, preload: :author}} + def match_action(:video, :self), + do: {:ok, %{target: Video, reactor: Video, preload: :author, viewer: VideoViewer}} def match_action(:video, :community), do: {:ok, %{target: Video, reactor: Community, flag: VideoCommunityFlag}} + def match_action(:video, :favorite), + do: {:ok, %{target: Video, reactor: VideoFavorite, preload: :user}} + + def match_action(:video, :star), + do: {:ok, %{target: Video, reactor: VideoStar, preload: :user}} + + def match_action(:video, :comment), + do: {:ok, %{target: Video, reactor: VideoComment, preload: :author}} + + def match_action(:video_comment, :like), + do: {:ok, %{target: VideoComment, reactor: VideoCommentLike}} + + def match_action(:video_comment, :dislike), + do: {:ok, %{target: VideoComment, reactor: VideoCommentDislike}} + ######################################### ## repos ... ######################################### - def match_action(:repo, :self), do: {:ok, %{target: Repo, reactor: Repo, preload: :author}} + def match_action(:repo, :self), + do: {:ok, %{target: Repo, reactor: Repo, preload: :author, viewer: RepoViewer}} def match_action(:repo, :community), do: {:ok, %{target: Repo, reactor: Community, flag: RepoCommunityFlag}} + def match_action(:repo, :favorite), + do: {:ok, %{target: Repo, reactor: RepoFavorite, preload: :user}} + + def match_action(:repo, :comment), + do: {:ok, %{target: Repo, reactor: RepoComment, preload: :author}} + + def match_action(:repo_comment, :like), + do: {:ok, %{target: RepoComment, reactor: RepoCommentLike}} + + def match_action(:repo_comment, :dislike), + do: {:ok, %{target: RepoComment, reactor: RepoCommentDislike}} + + # dynamic where query match def dynamic_where(thread, id) do case thread do :post -> @@ -110,6 +170,18 @@ defmodule MastaniServer.CMS.Utils.Matcher do :job_comment -> {:ok, dynamic([p], p.job_comment_id == ^id)} + :video -> + {:ok, dynamic([p], p.video_id == ^id)} + + :video_comment -> + {:ok, dynamic([p], p.video_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/mastani_server/cms/video.ex b/lib/mastani_server/cms/video.ex index 53618da88..dc9664ee5 100644 --- a/lib/mastani_server/cms/video.ex +++ b/lib/mastani_server/cms/video.ex @@ -4,7 +4,17 @@ defmodule MastaniServer.CMS.Video do use Ecto.Schema import Ecto.Changeset - alias MastaniServer.CMS.{Author, Community, VideoCommunityFlag, Tag} + + alias MastaniServer.CMS.{ + Author, + Community, + VideoComment, + VideoFavorite, + VideoCommunityFlag, + VideoStar, + VideoViewer, + Tag + } @required_fields ~w(title poster thumbnil desc duration duration_sec source link original_author original_author_link publish_at)a # @optional_fields ~w()a @@ -17,7 +27,7 @@ defmodule MastaniServer.CMS.Video do field(:desc, :string) field(:duration, :string) field(:duration_sec, :integer) - + belongs_to(:author, Author) field(:source, :string) field(:link, :string) @@ -25,6 +35,7 @@ defmodule MastaniServer.CMS.Video do field(:original_author_link, :string) field(:views, :integer, default: 0) + field(:publish_at, :utc_datetime) has_many(:community_flags, {"videos_communities_flags", VideoCommunityFlag}) @@ -32,11 +43,10 @@ defmodule MastaniServer.CMS.Video do field(:pin, :boolean, default_value: false) field(:trash, :boolean, default_value: false) - field(:publish_at, :utc_datetime) - - belongs_to(:author, Author) - - # has_many(:comments, {"posts_comments", PostComment}) + has_many(:favorites, {"videos_favorites", VideoFavorite}) + has_many(:stars, {"videos_stars", VideoStar}) + has_many(:viewers, {"videos_viewers", VideoViewer}) + has_many(:comments, {"videos_comments", VideoComment}) many_to_many( :tags, diff --git a/lib/mastani_server/cms/video_comment.ex b/lib/mastani_server/cms/video_comment.ex new file mode 100644 index 000000000..f7dd9a36c --- /dev/null +++ b/lib/mastani_server/cms/video_comment.ex @@ -0,0 +1,43 @@ +defmodule MastaniServer.CMS.VideoComment do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.Accounts + + alias MastaniServer.CMS.{ + Video, + VideoCommentDislike, + VideoCommentLike, + VideoCommentReply + } + + @required_fields ~w(body author_id video_id floor)a + @optional_fields ~w(reply_id)a + + @type t :: %VideoComment{} + schema "videos_comments" do + field(:body, :string) + field(:floor, :integer) + belongs_to(:author, Accounts.User, foreign_key: :author_id) + belongs_to(:video, Video, foreign_key: :video_id) + belongs_to(:reply_to, VideoComment, foreign_key: :reply_id) + + has_many(:replies, {"videos_comments_replies", VideoCommentReply}) + has_many(:likes, {"videos_comments_likes", VideoCommentLike}) + has_many(:dislikes, {"videos_comments_dislikes", VideoCommentDislike}) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%VideoComment{} = video_comment, attrs) do + video_comment + |> cast(attrs, @required_fields ++ @optional_fields) + |> validate_required(@required_fields) + |> validate_length(:body, min: 1) + |> foreign_key_constraint(:video_id) + |> foreign_key_constraint(:author_id) + end +end diff --git a/lib/mastani_server/cms/video_comment_dislike.ex b/lib/mastani_server/cms/video_comment_dislike.ex new file mode 100644 index 000000000..9113dcbf1 --- /dev/null +++ b/lib/mastani_server/cms/video_comment_dislike.ex @@ -0,0 +1,29 @@ +defmodule MastaniServer.CMS.VideoCommentDislike do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.Accounts + alias MastaniServer.CMS.VideoComment + + @required_fields ~w(video_comment_id user_id)a + + @type t :: %VideoCommentDislike{} + schema "videos_comments_dislikes" do + belongs_to(:user, Accounts.User, foreign_key: :user_id) + belongs_to(:video_comment, VideoComment, foreign_key: :video_comment_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%VideoCommentDislike{} = video_comment_dislike, attrs) do + video_comment_dislike + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + |> foreign_key_constraint(:video_comment_id) + |> foreign_key_constraint(:user_id) + |> unique_constraint(:user_id, name: :videos_comments_dislikes_user_id_video_comment_id_index) + end +end diff --git a/lib/mastani_server/cms/video_comment_like.ex b/lib/mastani_server/cms/video_comment_like.ex new file mode 100644 index 000000000..6c1ba97eb --- /dev/null +++ b/lib/mastani_server/cms/video_comment_like.ex @@ -0,0 +1,29 @@ +defmodule MastaniServer.CMS.VideoCommentLike do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.Accounts + alias MastaniServer.CMS.VideoComment + + @required_fields ~w(video_comment_id user_id)a + + @type t :: %VideoCommentLike{} + schema "videos_comments_likes" do + belongs_to(:user, Accounts.User, foreign_key: :user_id) + belongs_to(:video_comment, VideoComment, foreign_key: :video_comment_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%VideoCommentLike{} = video_comment_like, attrs) do + video_comment_like + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + |> foreign_key_constraint(:video_comment_id) + |> foreign_key_constraint(:user_id) + |> unique_constraint(:user_id, name: :videos_comments_likes_user_id_video_comment_id_index) + end +end diff --git a/lib/mastani_server/cms/video_comment_reply.ex b/lib/mastani_server/cms/video_comment_reply.ex new file mode 100644 index 000000000..0b68fc4b8 --- /dev/null +++ b/lib/mastani_server/cms/video_comment_reply.ex @@ -0,0 +1,27 @@ +defmodule MastaniServer.CMS.VideoCommentReply do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.CMS.VideoComment + + @required_fields ~w(video_comment_id reply_id)a + + @type t :: %VideoCommentReply{} + schema "videos_comments_replies" do + belongs_to(:video_comment, VideoComment, foreign_key: :video_comment_id) + belongs_to(:reply, VideoComment, foreign_key: :reply_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%VideoCommentReply{} = video_comment_reply, attrs) do + video_comment_reply + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + |> foreign_key_constraint(:video_comment_id) + |> foreign_key_constraint(:reply_id) + end +end diff --git a/lib/mastani_server/cms/video_community_flag.ex b/lib/mastani_server/cms/video_community_flag.ex index 3ab06dce5..94382031d 100644 --- a/lib/mastani_server/cms/video_community_flag.ex +++ b/lib/mastani_server/cms/video_community_flag.ex @@ -7,7 +7,7 @@ defmodule MastaniServer.CMS.VideoCommunityFlag do alias MastaniServer.CMS.{Community, Video} @required_fields ~w(video_id community_id)a - @optional_fields ~w(pin trash)a + @optional_fields ~w(trash)a @type t :: %VideoCommunityFlag{} @@ -15,7 +15,6 @@ defmodule MastaniServer.CMS.VideoCommunityFlag do belongs_to(:video, Video, foreign_key: :video_id) belongs_to(:community, Community, foreign_key: :community_id) - field(:pin, :boolean) field(:trash, :boolean) timestamps(type: :utc_datetime) diff --git a/lib/mastani_server/cms/video_favorite.ex b/lib/mastani_server/cms/video_favorite.ex new file mode 100644 index 000000000..dfbc9a3b7 --- /dev/null +++ b/lib/mastani_server/cms/video_favorite.ex @@ -0,0 +1,30 @@ +defmodule MastaniServer.CMS.VideoFavorite do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.Accounts + alias MastaniServer.CMS.Video + + @required_fields ~w(user_id video_id)a + @optional_fields ~w(category_id)a + + @type t :: %VideoFavorite{} + schema "videos_favorites" do + belongs_to(:user, Accounts.User, foreign_key: :user_id) + belongs_to(:video, Video, foreign_key: :video_id) + + belongs_to(:category, Accounts.FavoriteCategory) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%VideoFavorite{} = video_favorite, attrs) do + video_favorite + |> cast(attrs, @optional_fields ++ @required_fields) + |> validate_required(@required_fields) + |> unique_constraint(:user_id, name: :videos_favorites_user_id_video_id_index) + end +end diff --git a/lib/mastani_server/cms/video_star.ex b/lib/mastani_server/cms/video_star.ex new file mode 100644 index 000000000..b788d9d65 --- /dev/null +++ b/lib/mastani_server/cms/video_star.ex @@ -0,0 +1,27 @@ +defmodule MastaniServer.CMS.VideoStar do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.Accounts + alias MastaniServer.CMS.Video + + @required_fields ~w(user_id video_id)a + + @type t :: %VideoStar{} + schema "videos_stars" do + belongs_to(:user, Accounts.User, foreign_key: :user_id) + belongs_to(:video, Video, foreign_key: :video_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%VideoStar{} = video_star, attrs) do + video_star + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + |> unique_constraint(:user_id, name: :videos_stars_user_id_video_id_index) + end +end diff --git a/lib/mastani_server/cms/video_viewer.ex b/lib/mastani_server/cms/video_viewer.ex new file mode 100644 index 000000000..1b02e9e79 --- /dev/null +++ b/lib/mastani_server/cms/video_viewer.ex @@ -0,0 +1,27 @@ +defmodule MastaniServer.CMS.VideoViewer do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + alias MastaniServer.Accounts + alias MastaniServer.CMS.Video + + @required_fields ~w(video_id user_id)a + + @type t :: %VideoViewer{} + schema "videos_viewers" do + belongs_to(:video, Video, foreign_key: :video_id) + belongs_to(:user, Accounts.User, foreign_key: :user_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%VideoViewer{} = video_viewer, attrs) do + video_viewer + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + |> unique_constraint(:user_id, name: :videos_viewers_video_id_user_id_index) + end +end diff --git a/lib/mastani_server/statistics/delegates/geo.ex b/lib/mastani_server/statistics/delegates/geo.ex new file mode 100644 index 000000000..7a0b9c628 --- /dev/null +++ b/lib/mastani_server/statistics/delegates/geo.ex @@ -0,0 +1,24 @@ +defmodule MastaniServer.Statistics.Delegate.Geo do + @moduledoc """ + geo info settings + """ + import Ecto.Query, warn: false + import Helper.Utils + import ShortMaps + + alias Helper.ORM + alias MastaniServer.Statistics.UserGeoInfo + + def inc_count(city) do + with {:ok, geo_info} <- UserGeoInfo |> ORM.find_by(~m(city)a) do + geo_info |> ORM.update(%{value: geo_info.value + 1}) + end + end + + def list_cities_info do + UserGeoInfo + |> where([g], g.value > 0) + |> ORM.paginater(page: 1, size: 300) + |> done() + end +end diff --git a/lib/mastani_server/statistics/statistics.ex b/lib/mastani_server/statistics/statistics.ex index 11759fd1c..455044ee6 100644 --- a/lib/mastani_server/statistics/statistics.ex +++ b/lib/mastani_server/statistics/statistics.ex @@ -5,14 +5,21 @@ defmodule MastaniServer.Statistics do alias MastaniServer.Statistics.Delegate.{ Contribute, - Throttle + Throttle, + Geo } + # contributes defdelegate make_contribute(info), to: Contribute defdelegate list_contributes(info), to: Contribute defdelegate list_contributes_digest(community), to: Contribute + # publish Throttle defdelegate log_publish_action(user), to: Throttle defdelegate load_throttle_record(user), to: Throttle defdelegate mock_throttle_attr(scope, user, opt), to: Throttle + + # geo + defdelegate inc_count(city), to: Geo + defdelegate list_cities_info(), to: Geo end diff --git a/lib/mastani_server/statistics/user_geo_info.ex b/lib/mastani_server/statistics/user_geo_info.ex new file mode 100644 index 000000000..f1febd4fa --- /dev/null +++ b/lib/mastani_server/statistics/user_geo_info.ex @@ -0,0 +1,28 @@ +defmodule MastaniServer.Statistics.UserGeoInfo do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + # alias MastaniServer.Accounts.User + + @required_fields ~w(city long lant)a + @optional_fields ~w(value)a + + @type t :: %UserGeoInfo{} + schema "geos" do + field(:city, :string) + field(:long, :float) + field(:lant, :float) + field(:value, :integer, default: 0) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%UserGeoInfo{} = user_geo_info, attrs) do + user_geo_info + |> cast(attrs, @optional_fields ++ @required_fields) + |> validate_required(@required_fields) + end +end diff --git a/lib/mastani_server_web/context.ex b/lib/mastani_server_web/context.ex index 46cdd00c4..e162e8ade 100644 --- a/lib/mastani_server_web/context.ex +++ b/lib/mastani_server_web/context.ex @@ -26,9 +26,10 @@ defmodule MastaniServerWeb.Context do authorization header is sent. """ def build_context(conn) do + # IO.inspect conn.remote_ip, label: "conn" with ["Bearer " <> token] <- get_req_header(conn, "authorization"), {:ok, cur_user} <- authorize(token) do - %{cur_user: cur_user} + %{cur_user: cur_user, remote_ip: conn.remote_ip} else _ -> %{} end diff --git a/lib/mastani_server_web/middleware/cut_participators.ex b/lib/mastani_server_web/middleware/cut_participators.ex new file mode 100644 index 000000000..bdf3082f4 --- /dev/null +++ b/lib/mastani_server_web/middleware/cut_participators.ex @@ -0,0 +1,23 @@ +defmodule MastaniServerWeb.Middleware.CutParticipators do + @moduledoc """ + # cut comments participators manually by count + # this tem solution may have performace issue when the content's comments + # has too much participators + # + # NOTE: this is NOT the right solution + # should use WINDOW function + # see https://github.com/coderplanets/coderplanets_server/issues/16 + # + """ + + @behaviour Absinthe.Middleware + @count 5 + + def call(%{errors: errors} = resolution, _) when length(errors) > 0, do: resolution + + def call(%{value: value} = resolution, _) do + %{resolution | value: value |> Enum.slice(0, @count)} + end + + def call(resolution, _), do: resolution +end diff --git a/lib/mastani_server_web/middleware/force_loader.ex b/lib/mastani_server_web/middleware/force_loader.ex index 85475086b..9e1fb6486 100644 --- a/lib/mastani_server_web/middleware/force_loader.ex +++ b/lib/mastani_server_web/middleware/force_loader.ex @@ -1,8 +1,9 @@ -# this is a tmp solution for load related-users like situations -# it turn dataloader into nomal N+1 resolver -# NOTE: it should be replaced using "Select-Top-N-By-Group" solution - defmodule MastaniServerWeb.Middleware.ForceLoader do + @moduledoc """ + # this is a tmp solution for load related-users like situations + # it turn dataloader into nomal N+1 resolver + # NOTE: it should be replaced using "Select-Top-N-By-Group" solution + """ @behaviour Absinthe.Middleware def call(%{source: %{id: id}} = resolution, _) do diff --git a/lib/mastani_server_web/middleware/pagesize_proof.ex b/lib/mastani_server_web/middleware/pagesize_proof.ex index 77533cd87..9a6901a60 100644 --- a/lib/mastani_server_web/middleware/pagesize_proof.ex +++ b/lib/mastani_server_web/middleware/pagesize_proof.ex @@ -41,7 +41,9 @@ defmodule MastaniServerWeb.Middleware.PageSizeProof do defp valid_size(%{filter: %{first: size}} = arg), do: do_size_check(size, arg) defp valid_size(%{filter: %{size: size}} = arg), do: do_size_check(size, arg) - defp valid_size(arg), do: arg |> Map.merge(%{filter: %{first: @inner_page_size}}) + # defp valid_size(arg), do: arg |> Map.merge(%{filter: %{first: @inner_page_size}}) + defp valid_size(arg), + do: arg |> Map.merge(%{filter: %{page: 1, size: @max_page_size, first: @inner_page_size}}) defp do_size_check(size, arg) do case size in 1..@max_page_size do diff --git a/lib/mastani_server_web/middleware/passport.ex b/lib/mastani_server_web/middleware/passport.ex index 36621a5e1..2c0896a2c 100644 --- a/lib/mastani_server_web/middleware/passport.ex +++ b/lib/mastani_server_web/middleware/passport.ex @@ -16,7 +16,9 @@ defmodule MastaniServerWeb.Middleware.Passport do import Helper.Utils import Helper.ErrorCode - def call(%{errors: errors} = resolution, _) when length(errors) > 0, do: resolution + def call(%{errors: errors} = resolution, _) when length(errors) > 0 do + resolution + end def call(%{arguments: %{passport_is_owner: true}} = resolution, claim: "owner"), do: resolution @@ -131,13 +133,15 @@ defmodule MastaniServerWeb.Middleware.Passport do defp cp_check(resolution, claim) do cur_passport = resolution.context.cur_user.cur_passport - community_title = resolution.arguments.passport_communities |> List.first() |> Map.get(:title) + # community_title = resolution.arguments.passport_communities |> List.first() |> Map.get(:title) + community_raw = resolution.arguments.passport_communities |> List.first() |> Map.get(:raw) thread = resolution.arguments.thread |> to_string path = claim - |> String.replace("c?", community_title) + # |> String.replace("c?", community_title) + |> String.replace("c?", community_raw) |> String.replace("t?", thread) |> String.split("->") diff --git a/lib/mastani_server_web/resolvers/accounts_resolver.ex b/lib/mastani_server_web/resolvers/accounts_resolver.ex index 304082e7b..9f7fd0d0d 100644 --- a/lib/mastani_server_web/resolvers/accounts_resolver.ex +++ b/lib/mastani_server_web/resolvers/accounts_resolver.ex @@ -3,13 +3,22 @@ defmodule MastaniServerWeb.Resolvers.Accounts do accounts resolvers """ import ShortMaps + import Helper.ErrorCode alias Helper.{Certification, ORM} alias MastaniServer.{Accounts, CMS} alias Accounts.{MentionMail, NotificationMail, SysNotificationMail, User} - def user(_root, %{id: id}, _info), do: User |> ORM.find(id) + def user(_root, %{id: id}, _info), do: User |> ORM.read(id, inc: :views) + + def user(_root, _args, %{context: %{cur_user: cur_user}}), + do: User |> ORM.read(cur_user.id, inc: :views) + + def user(_root, _args, _info) do + {:error, [message: "need login", code: ecode(:account_login)]} + end + def users(_root, ~m(filter)a, _info), do: User |> ORM.find_all(filter) def session_state(_root, _args, %{context: %{cur_user: cur_user}}), @@ -17,16 +26,39 @@ defmodule MastaniServerWeb.Resolvers.Accounts do def session_state(_root, _args, _info), do: {:ok, %{is_valid: false}} - def account(_root, _args, %{context: %{cur_user: cur_user}}) do - User |> ORM.find(cur_user.id) - end + def update_profile(_root, args, %{context: %{cur_user: cur_user}}) do + profile = + if Map.has_key?(args, :education_backgrounds), + do: Map.merge(args.profile, %{education_backgrounds: args.education_backgrounds}), + else: args.profile + + profile = + if Map.has_key?(args, :work_backgrounds), + do: Map.merge(profile, %{work_backgrounds: args.work_backgrounds}), + else: profile - def update_profile(_root, %{profile: profile}, %{context: %{cur_user: cur_user}}) do Accounts.update_profile(%User{id: cur_user.id}, profile) end - def github_signin(_root, %{github_user: github_user}, _info) do - Accounts.github_signin(github_user) + def github_signin(_root, %{github_user: github_user}, %{remote_ip: remote_ip}) do + # IO.inspect(remote_ip, label: "remote_ip") + Accounts.github_signin(github_user, remote_ip) + end + + def get_customization(_root, _args, %{context: %{cur_user: cur_user}}) do + Accounts.get_customization(cur_user) + end + + # def set_customization(_root, ~m(user_id customization)a, %{context: %{cur_user: cur_user}}) do + # Accounts.set_customization(%User{id: user_id}, customization) + # end + + def set_customization(_root, ~m(customization)a, %{context: %{cur_user: cur_user}}) do + Accounts.set_customization(cur_user, customization) + end + + def set_customization(_root, _args, _info) do + {:error, [message: "need login", code: ecode(:account_login)]} end def list_favorite_categories(_root, %{filter: filter}, %{context: %{cur_user: cur_user}}) do @@ -81,21 +113,57 @@ defmodule MastaniServerWeb.Resolvers.Accounts do Accounts.fetch_followings(cur_user, filter) end - # for check other users query - def favorited_posts(_root, ~m(user_id filter)a, _info) do - Accounts.reacted_contents(:post, :favorite, filter, %User{id: user_id}) + # 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}) + end + + def favorited_contents(_root, ~m(user_id filter thread)a, _info) do + Accounts.reacted_contents(thread, :favorite, filter, %User{id: user_id}) end - def favorited_posts(_root, ~m(filter)a, %{context: %{cur_user: cur_user}}) do - Accounts.reacted_contents(:post, :favorite, filter, cur_user) + def favorited_contents(_root, ~m(filter thread)a, %{context: %{cur_user: cur_user}}) do + Accounts.reacted_contents(thread, :favorite, filter, cur_user) end - def favorited_jobs(_root, ~m(user_id filter)a, _info) do - Accounts.reacted_contents(:job, :favorite, filter, %User{id: user_id}) + # 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}) end - def favorited_jobs(_root, ~m(filter)a, %{context: %{cur_user: cur_user}}) do - Accounts.reacted_contents(:job, :favorite, filter, cur_user) + def stared_contents(_root, ~m(filter thread)a, %{context: %{cur_user: cur_user}}) do + Accounts.reacted_contents(thread, :star, filter, cur_user) + end + + # published contents + def published_contents(_root, ~m(user_id filter thread)a, _info) do + Accounts.published_contents(%User{id: user_id}, thread, filter) + end + + def published_contents(_root, ~m(filter thread)a, %{context: %{cur_user: cur_user}}) do + Accounts.published_contents(cur_user, thread, filter) + end + + # published comments + def published_comments(_root, ~m(user_id filter thread)a, _info) do + Accounts.published_comments(%User{id: user_id}, thread, filter) + end + + def published_comments(_root, ~m(filter thread)a, %{context: %{cur_user: cur_user}}) do + Accounts.published_comments(cur_user, thread, filter) + end + + # paged communities which the user it's the editor + def editable_communities(_root, ~m(user_id filter)a, _info) do + Accounts.list_editable_communities(%User{id: user_id}, filter) + end + + def editable_communities(_root, ~m(filter)a, %{context: %{cur_user: cur_user}}) do + Accounts.list_editable_communities(cur_user, filter) + end + + def editable_communities(root, ~m(filter)a, _info) do + Accounts.list_editable_communities(%User{id: root.id}, filter) end # TODO: refactor @@ -138,11 +206,14 @@ defmodule MastaniServerWeb.Resolvers.Accounts do end # for user self's - def subscribed_communities(_root, %{filter: filter}, %{cur_user: cur_user}) do + def subscribed_communities(_root, %{filter: filter}, %{context: %{cur_user: cur_user}}) do Accounts.subscribed_communities(%User{id: cur_user.id}, filter) end - # + def subscribed_communities(%{id: id}, %{filter: filter}, _info) do + Accounts.subscribed_communities(%User{id: id}, filter) + end + def subscribed_communities(_root, %{user_id: "", filter: filter}, _info) do Accounts.default_subscribed_communities(filter) end @@ -161,12 +232,8 @@ defmodule MastaniServerWeb.Resolvers.Accounts do end def get_passport_string(root, _args, %{context: %{cur_user: _}}) do - case CMS.get_passport(%User{id: root.id}) do - {:ok, passport} -> - {:ok, Jason.encode!(passport)} - - {:error, _} -> - {:ok, nil} + with {:ok, passport} <- CMS.get_passport(%User{id: root.id}) do + {:ok, Jason.encode!(passport)} end end diff --git a/lib/mastani_server_web/resolvers/cms_resolver.ex b/lib/mastani_server_web/resolvers/cms_resolver.ex index ee32510f0..4be8c03ea 100644 --- a/lib/mastani_server_web/resolvers/cms_resolver.ex +++ b/lib/mastani_server_web/resolvers/cms_resolver.ex @@ -30,12 +30,34 @@ defmodule MastaniServerWeb.Resolvers.CMS do def delete_community(_root, %{id: id}, _info), do: Community |> ORM.find_delete(id) # ####################### - # community thread (post, job) + # community thread (post, job), login user should be logged # ####################### + def post(_root, %{id: id}, %{context: %{cur_user: user}}) do + CMS.read_content(:post, id, user) + end + def post(_root, %{id: id}, _info), do: Post |> ORM.read(id, inc: :views) + + def job(_root, %{id: id}, %{context: %{cur_user: user}}) do + CMS.read_content(:job, id, user) + end + + def job(_root, %{id: id}, _info), do: Job |> ORM.read(id, inc: :views) + + def video(_root, %{id: id}, %{context: %{cur_user: user}}) do + CMS.read_content(:video, id, user) + end + def video(_root, %{id: id}, _info), do: Video |> ORM.read(id, inc: :views) + + def repo(_root, %{id: id}, %{context: %{cur_user: user}}) do + CMS.read_content(:repo, id, user) + end + def repo(_root, %{id: id}, _info), do: Repo |> ORM.read(id, inc: :views) - def job(_root, %{id: id}, _info), do: Job |> ORM.read(id, inc: :views) + + def wiki(_root, ~m(community)a, _info), do: CMS.get_wiki(%Community{raw: community}) + def cheatsheet(_root, ~m(community)a, _info), do: CMS.get_cheatsheet(%Community{raw: community}) def paged_posts(_root, ~m(filter)a, _info), do: Post |> CMS.paged_contents(filter) def paged_videos(_root, ~m(filter)a, _info), do: Video |> CMS.paged_contents(filter) @@ -46,19 +68,51 @@ defmodule MastaniServerWeb.Resolvers.CMS do CMS.create_content(%Community{id: community_id}, thread, args, user) end - def update_content(_root, %{passport_source: content} = args, _info), - do: ORM.update(content, args) + def update_content(_root, %{passport_source: content} = args, _info) do + ORM.update(content, args) + end def delete_content(_root, %{passport_source: content}, _info), do: ORM.delete(content) # ####################### # content flag .. # ####################### - def pin_content(_root, ~m(id thread community_id)a, _info), - do: set_community_flags(community_id, thread, id, %{pin: true}) + def pin_content(_root, ~m(id community_id thread topic)a, _info) do + CMS.pin_content(%CMS.Post{id: id}, %Community{id: community_id}, topic) + end + + def undo_pin_content(_root, ~m(id community_id thread topic)a, _info) do + CMS.undo_pin_content(%CMS.Post{id: id}, %Community{id: community_id}, topic) + end + + def pin_content(_root, ~m(id community_id thread)a, _info) do + do_pin_content(id, community_id, thread) + end + + def undo_pin_content(_root, ~m(id community_id thread)a, _info) do + do_undo_pin_content(id, community_id, thread) + end - def undo_pin_content(_root, ~m(id thread community_id)a, _info), - do: set_community_flags(community_id, thread, id, %{pin: false}) + def do_pin_content(id, community_id, :job), + do: CMS.pin_content(%CMS.Job{id: id}, %Community{id: community_id}) + + def do_pin_content(id, community_id, :video), + do: CMS.pin_content(%CMS.Video{id: id}, %Community{id: community_id}) + + def do_pin_content(id, community_id, :repo), + do: CMS.pin_content(%CMS.Repo{id: id}, %Community{id: community_id}) + + def do_undo_pin_content(id, community_id, :job) do + CMS.undo_pin_content(%CMS.Job{id: id}, %Community{id: community_id}) + end + + def do_undo_pin_content(id, community_id, :video) do + CMS.undo_pin_content(%CMS.Video{id: id}, %Community{id: community_id}) + end + + def do_undo_pin_content(id, community_id, :repo) do + CMS.undo_pin_content(%CMS.Repo{id: id}, %Community{id: community_id}) + end def trash_content(_root, ~m(id thread community_id)a, _info), do: set_community_flags(community_id, thread, id, %{trash: true}) @@ -66,6 +120,13 @@ defmodule MastaniServerWeb.Resolvers.CMS do def undo_trash_content(_root, ~m(id thread community_id)a, _info), do: set_community_flags(community_id, thread, id, %{trash: false}) + # TODO: report contents + # def report_content(_root, ~m(id thread community_id)a, _info), + # do: set_community_flags(community_id, thread, id, %{report: true}) + + # def undo_report_content(_root, ~m(id thread community_id)a, _info), + # do: set_community_flags(community_id, thread, id, %{report: false}) + defp set_community_flags(community_id, thread, id, flag) do with {:ok, content} <- match_action(thread, :self) do content.target @@ -89,6 +150,10 @@ defmodule MastaniServerWeb.Resolvers.CMS do CMS.reaction_users(thread, action, id, filter) end + def favorited_category(root, ~m(thread)a, %{context: %{cur_user: user}}) do + CMS.favorited_category(thread, root.id, user) + end + # ####################### # category .. # ####################### @@ -147,6 +212,13 @@ defmodule MastaniServerWeb.Resolvers.CMS do CMS.community_members(:editors, %Community{id: id}, filter) end + # ####################### + # geo infos .. + # ####################### + def community_geo_info(_root, ~m(id)a, _info) do + CMS.community_geo_info(%Community{id: id}) + end + # ####################### # tags .. # ####################### @@ -165,6 +237,14 @@ defmodule MastaniServerWeb.Resolvers.CMS do def unset_tag(_root, ~m(id thread tag_id)a, _info), do: CMS.unset_tag(thread, %Tag{id: tag_id}, id) + def get_tags(_root, ~m(community_id thread topic)a, _info) do + CMS.get_tags(%Community{id: community_id}, thread, topic) + end + + def get_tags(_root, ~m(community thread topic)a, _info) do + CMS.get_tags(%Community{raw: community}, thread, topic) + end + def get_tags(_root, ~m(community_id thread)a, _info) do CMS.get_tags(%Community{id: community_id}, thread) end @@ -182,8 +262,8 @@ defmodule MastaniServerWeb.Resolvers.CMS do # ####################### # community subscribe .. # ####################### - def subscribe_community(_root, ~m(community_id)a, %{context: %{cur_user: cur_user}}) do - CMS.subscribe_community(%Community{id: community_id}, cur_user) + def subscribe_community(_root, ~m(community_id)a, %{context: ~m(cur_user remote_ip)a}) do + CMS.subscribe_community(%Community{id: community_id}, cur_user, remote_ip) end def unsubscribe_community(_root, ~m(community_id)a, %{context: %{cur_user: cur_user}}) do @@ -205,8 +285,17 @@ defmodule MastaniServerWeb.Resolvers.CMS do # ####################### # comemnts .. # ####################### - def paged_comments(_root, ~m(id thread filter)a, _info), - do: CMS.list_comments(thread, id, filter) + def paged_comments(_root, ~m(id thread filter)a, _info) do + CMS.list_comments(thread, id, filter) + end + + def paged_comments_participators(_root, ~m(id thread filter)a, _info) do + CMS.list_comments_participators(thread, id, filter) + end + + def paged_comments_participators(root, ~m(thread)a, _info) do + CMS.list_comments_participators(thread, root.id, %{page: 1, size: 20}) + end def create_comment(_root, ~m(thread id body)a, %{context: %{cur_user: user}}) do CMS.create_comment(thread, id, body, user) @@ -239,4 +328,23 @@ defmodule MastaniServerWeb.Resolvers.CMS do def stamp_passport(_root, ~m(user_id rules)a, %{context: %{cur_user: _user}}) do CMS.stamp_passport(rules, %User{id: user_id}) end + + # ####################### + # sync github content .. + # ####################### + def sync_wiki(_root, ~m(community_id readme last_sync)a, %{context: %{cur_user: _user}}) do + CMS.sync_github_content(%Community{id: community_id}, :wiki, ~m(readme last_sync)a) + end + + def add_wiki_contributor(_root, ~m(id contributor)a, %{context: %{cur_user: _user}}) do + CMS.add_contributor(%CMS.CommunityWiki{id: id}, contributor) + end + + def sync_cheatsheet(_root, ~m(community_id readme last_sync)a, %{context: %{cur_user: _user}}) do + CMS.sync_github_content(%Community{id: community_id}, :cheatsheet, ~m(readme last_sync)a) + end + + def add_cheatsheet_contributor(_root, ~m(id contributor)a, %{context: %{cur_user: _user}}) do + CMS.add_contributor(%CMS.CommunityCheatsheet{id: id}, contributor) + end end diff --git a/lib/mastani_server_web/resolvers/statistics_resolver.ex b/lib/mastani_server_web/resolvers/statistics_resolver.ex index 9b1fbb3c1..974889844 100644 --- a/lib/mastani_server_web/resolvers/statistics_resolver.ex +++ b/lib/mastani_server_web/resolvers/statistics_resolver.ex @@ -25,4 +25,8 @@ defmodule MastaniServerWeb.Resolvers.Statistics do def make_contrubute(_root, %{user_id: user_id}, _info) do Statistics.make_contribute(%Accounts.User{id: user_id}) end + + def list_cities_geo_info(_root, _args, _info) do + Statistics.list_cities_info() + end end diff --git a/lib/mastani_server_web/router.ex b/lib/mastani_server_web/router.ex index 0975abab9..598c1143c 100644 --- a/lib/mastani_server_web/router.ex +++ b/lib/mastani_server_web/router.ex @@ -4,6 +4,7 @@ defmodule MastaniServerWeb.Router do use Sentry.Plug pipeline :api do + plug(Helper.PublicIpPlug) plug(:accepts, ["json"]) plug(MastaniServerWeb.Context) end @@ -15,6 +16,7 @@ defmodule MastaniServerWeb.Router do "/", Absinthe.Plug.GraphiQL, schema: MastaniServerWeb.Schema, + # json_codec: Jason, pipeline: {ApolloTracing.Pipeline, :plug}, interface: :playground, context: %{pubsub: MastaniServerWeb.Endpoint} diff --git a/lib/mastani_server_web/schema/account/account_misc.ex b/lib/mastani_server_web/schema/account/account_misc.ex index 5d69fc9be..933f4957f 100644 --- a/lib/mastani_server_web/schema/account/account_misc.ex +++ b/lib/mastani_server_web/schema/account/account_misc.ex @@ -32,16 +32,16 @@ defmodule MastaniServerWeb.Schema.Account.Misc do field(:public_gists, :integer) end - input_object :education_background do - field(:school, :string) - field(:major, :string) - end - - input_object :work_background do + input_object :work_background_input do field(:company, :string) field(:title, :string) end + input_object :edu_background_input do + field(:school, :string) + field(:major, :string) + end + input_object :user_profile_input do field(:nickname, :string) field(:bio, :string) @@ -50,9 +50,28 @@ defmodule MastaniServerWeb.Schema.Account.Misc do field(:email, :string) # social sscial_fields() - # backgrounds - field(:education_backgrounds, list_of(:education_background)) - field(:work_backgrounds, list_of(:work_background)) + end + + enum :cus_banner_layout_num do + value(:digest) + value(:brief) + end + + enum :cus_contents_layout_num do + value(:digest) + value(:list) + end + + input_object :customization_input do + field(:theme, :string) + field(:community_chart, :boolean) + field(:brainwash_free, :boolean) + + field(:banner_layout, :cus_banner_layout_num, default_value: :digest) + field(:contents_layout, :cus_contents_layout_num, default_value: :digest) + field(:content_divider, :boolean) + field(:mark_viewed, :boolean) + field(:display_density, :string, default_value: "20") end # see: https://github.com/absinthe-graphql/absinthe/issues/206 diff --git a/lib/mastani_server_web/schema/account/account_mutations.ex b/lib/mastani_server_web/schema/account/account_mutations.ex index f7ce6839f..5f92625d9 100644 --- a/lib/mastani_server_web/schema/account/account_mutations.ex +++ b/lib/mastani_server_web/schema/account/account_mutations.ex @@ -18,6 +18,8 @@ defmodule MastaniServerWeb.Schema.Account.Mutations do @desc "update user's profile" field :update_profile, :user do arg(:profile, non_null(:user_profile_input)) + arg(:work_backgrounds, list_of(:work_background_input)) + arg(:education_backgrounds, list_of(:edu_background_input)) middleware(M.Authorize, :login) resolve(&R.Accounts.update_profile/3) @@ -25,7 +27,6 @@ defmodule MastaniServerWeb.Schema.Account.Mutations do field :github_signin, :token_info do arg(:code, non_null(:string)) - # arg(:profile, non_null(:github_profile_input)) middleware(M.GithubUser) resolve(&R.Accounts.github_signin/3) @@ -96,6 +97,14 @@ defmodule MastaniServerWeb.Schema.Account.Mutations do resolve(&R.Accounts.unset_favorites/3) end + @desc "set user's customization" + field :set_customization, :user do + arg(:user_id, :id) + arg(:customization, non_null(:customization_input)) + + resolve(&R.Accounts.set_customization/3) + end + @desc "mark a mention as read" field :mark_mention_read, :status do arg(:id, non_null(:id)) diff --git a/lib/mastani_server_web/schema/account/account_queries.ex b/lib/mastani_server_web/schema/account/account_queries.ex index 7e7fd2b65..c11a0f41b 100644 --- a/lib/mastani_server_web/schema/account/account_queries.ex +++ b/lib/mastani_server_web/schema/account/account_queries.ex @@ -15,7 +15,7 @@ defmodule MastaniServerWeb.Schema.Account.Queries do @desc "get user by id" field :user, :user do - arg(:id, non_null(:id)) + arg(:id, :id) resolve(&R.Accounts.user/3) end @@ -60,7 +60,7 @@ defmodule MastaniServerWeb.Schema.Account.Queries do end @desc "get favorites categoories" - field :list_favorite_categories, :paged_favorites_categories do + field :favorite_categories, :paged_favorites_categories do arg(:user_id, :id) arg(:filter, non_null(:common_paged_filter)) @@ -68,22 +68,167 @@ defmodule MastaniServerWeb.Schema.Account.Queries do resolve(&R.Accounts.list_favorite_categories/3) end + @doc "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) + + middleware(M.PageSizeProof) + resolve(&R.Accounts.stared_contents/3) + end + + @doc "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) + + middleware(M.PageSizeProof) + resolve(&R.Accounts.stared_contents/3) + end + + @doc "paged stared videos" + field :stared_videos, :paged_videos do + arg(:user_id, non_null(:id)) + arg(:filter, non_null(:paged_filter)) + arg(:thread, :video_thread, default_value: :video) + + middleware(M.PageSizeProof) + resolve(&R.Accounts.stared_contents/3) + end + @desc "get favorited posts" field :favorited_posts, :paged_posts do - arg(:user_id, :id) + 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_posts/3) + 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 videos" + field :favorited_videos, :paged_videos do + arg(:user_id, non_null(:id)) + arg(:filter, non_null(:paged_filter)) + arg(:category_id, :id) + arg(:thread, :video_thread, default_value: :video) + + 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) + end + + @desc "get paged published posts" + field :published_posts, :paged_posts do + arg(:user_id, non_null(:id)) + arg(:filter, non_null(:paged_filter)) + arg(:thread, :post_thread, default_value: :post) + + middleware(M.PageSizeProof) + resolve(&R.Accounts.published_contents/3) + end + + @desc "get paged published jobs" + field :published_jobs, :paged_jobs 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_contents/3) + end + + @desc "get paged published videos" + field :published_videos, :paged_videos do + arg(:user_id, non_null(:id)) + arg(:filter, non_null(:paged_filter)) + arg(:thread, :video_thread, default_value: :video) + + middleware(M.PageSizeProof) + resolve(&R.Accounts.published_contents/3) + end + + @desc "get paged published repos" + field :published_repos, :paged_repos 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_contents/3) + end + + @desc "get paged published comments on post" + field :published_post_comments, :paged_post_comments do + arg(:user_id, non_null(:id)) + arg(:filter, non_null(:paged_filter)) + arg(:thread, :post_thread, default_value: :post) + + middleware(M.PageSizeProof) + 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 video" + field :published_video_comments, :paged_video_comments do + arg(:user_id, non_null(:id)) + arg(:filter, non_null(:paged_filter)) + arg(:thread, :video_thread, default_value: :video) + + 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) arg(:filter, non_null(:paged_filter)) middleware(M.PageSizeProof) - resolve(&R.Accounts.favorited_jobs/3) + resolve(&R.Accounts.editable_communities/3) end @desc "get all passport rules include system and community etc ..." diff --git a/lib/mastani_server_web/schema/account/account_types.ex b/lib/mastani_server_web/schema/account/account_types.ex index 880ecdcfe..8dc1f492b 100644 --- a/lib/mastani_server_web/schema/account/account_types.ex +++ b/lib/mastani_server_web/schema/account/account_types.ex @@ -32,6 +32,9 @@ defmodule MastaniServerWeb.Schema.Account.Types do field(:sex, :string) field(:email, :string) field(:location, :string) + field(:geo_city, :string) + + field(:views, :integer) sscial_fields() @@ -41,6 +44,11 @@ defmodule MastaniServerWeb.Schema.Account.Types do field(:github_profile, :github_profile, resolve: dataloader(Accounts, :github_profile)) field(:achievement, :achievement, resolve: dataloader(Accounts, :achievement)) + field(:customization, :customization) do + middleware(M.Authorize, :login) + resolve(&R.Accounts.get_customization/3) + end + field(:education_backgrounds, list_of(:education_background)) field(:work_backgrounds, list_of(:work_background)) @@ -62,11 +70,18 @@ defmodule MastaniServerWeb.Schema.Account.Types do resolve(&R.Accounts.get_passport/3) end - field :subscribed_communities, list_of(:community) do - arg(:filter, :members_filter) + # field :subscribed_communities, list_of(:community) do + # arg(:filter, :members_filter) + + # middleware(M.PageSizeProof) + # resolve(dataloader(Accounts, :subscribed_communities)) + # end + @desc "paged communities subscribed by this user" + field :subscribed_communities, :paged_communities do + arg(:filter, :paged_filter) middleware(M.PageSizeProof) - resolve(dataloader(Accounts, :subscribed_communities)) + resolve(&R.Accounts.subscribed_communities/3) end field :subscribed_communities_count, :integer do @@ -76,6 +91,17 @@ defmodule MastaniServerWeb.Schema.Account.Types do middleware(M.ConvertToInt) end + @desc "paged communities which the user it's the editor" + field :editable_communities, :paged_communities do + # arg(:filter, non_null(:paged_filter)) + arg(:filter, :paged_filter) + + # middleware(M.SeeMe) + middleware(M.PageSizeProof) + resolve(&R.Accounts.editable_communities/3) + end + + @doc "get follower users count" field :followers_count, :integer do arg(:count, :count_type, default_value: :count) @@ -83,6 +109,7 @@ defmodule MastaniServerWeb.Schema.Account.Types do middleware(M.ConvertToInt) end + @doc "get following users count" field :followings_count, :integer do arg(:count, :count_type, default_value: :count) @@ -90,6 +117,7 @@ defmodule MastaniServerWeb.Schema.Account.Types do middleware(M.ConvertToInt) end + @doc "wether viewer has followed" field :viewer_has_followed, :boolean do arg(:viewer_did, :viewer_did_type, default_value: :viewer_did) @@ -99,20 +127,94 @@ defmodule MastaniServerWeb.Schema.Account.Types do middleware(M.ViewerDidConvert) end + @doc "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 + + @doc "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 + + @doc "paged stared videos" + field :stared_videos, :paged_videos do + arg(:filter, non_null(:paged_filter)) + arg(:thread, :video_thread, default_value: :video) + + middleware(M.PageSizeProof) + resolve(&R.Accounts.stared_contents/3) + end + + @doc "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_posts/3) + resolve(&R.Accounts.favorited_contents/3) end + @doc "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_jobs/3) + resolve(&R.Accounts.favorited_contents/3) end + @doc "paged favorited videos" + field :favorited_videos, :paged_videos do + arg(:filter, non_null(:paged_filter)) + arg(:thread, :video_thread, default_value: :video) + + middleware(M.PageSizeProof) + resolve(&R.Accounts.favorited_contents/3) + end + + @doc "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 + + @doc "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 + + @doc "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 + + @doc "total count of stared videos count" + field :stared_videos_count, :integer do + arg(:count, :count_type, default_value: :count) + + resolve(dataloader(Accounts, :stared_videos)) + middleware(M.ConvertToInt) + end + + @doc "total count of favorited posts count" field :favorited_posts_count, :integer do arg(:count, :count_type, default_value: :count) @@ -120,6 +222,7 @@ defmodule MastaniServerWeb.Schema.Account.Types do middleware(M.ConvertToInt) end + @doc "total count of favorited jobs count" field :favorited_jobs_count, :integer do arg(:count, :count_type, default_value: :count) @@ -127,6 +230,22 @@ defmodule MastaniServerWeb.Schema.Account.Types do middleware(M.ConvertToInt) end + @doc "total count of favorited videos count" + field :favorited_videos_count, :integer do + arg(:count, :count_type, default_value: :count) + + resolve(dataloader(Accounts, :favorited_videos)) + middleware(M.ConvertToInt) + end + + @doc "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/3) end @@ -168,6 +287,19 @@ defmodule MastaniServerWeb.Schema.Account.Types do end end + # field(:sidebar_layout, :map) + object :customization do + field(:theme, :string) + field(:community_chart, :boolean) + field(:brainwash_free, :boolean) + + field(:banner_layout, :string) + field(:contents_layout, :string) + field(:content_divider, :boolean) + field(:mark_viewed, :boolean) + field(:display_density, :string) + end + object :github_profile do field(:id, :id) field(:github_id, :string) @@ -193,6 +325,9 @@ defmodule MastaniServerWeb.Schema.Account.Types do field(:index, :integer) field(:total_count, :integer) field(:private, :boolean) + field(:last_updated, :datetime) + field(:inserted_at, :datetime) + field(:updated_at, :datetime) end object :paged_favorites_categories do @@ -200,12 +335,22 @@ defmodule MastaniServerWeb.Schema.Account.Types do pagination_fields() end + object :source_contribute do + field(:web, :boolean) + field(:server, :boolean) + field(:we_app, :boolean) + field(:h5, :boolean) + field(:mobile, :boolean) + end + object :achievement do field(:reputation, :integer) - field(:followers_count, :integer) + # field(:followers_count, :integer) field(:contents_stared_count, :integer) field(:contents_favorited_count, :integer) - field(:contents_watched_count, :integer) + # field(:contents_watched_count, :integer) + + field(:source_contribute, :source_contribute) end object :token_info do diff --git a/lib/mastani_server_web/schema/cms/cms_misc.ex b/lib/mastani_server_web/schema/cms/cms_misc.ex index bd486888a..937af704d 100644 --- a/lib/mastani_server_web/schema/cms/cms_misc.ex +++ b/lib/mastani_server_web/schema/cms/cms_misc.ex @@ -17,9 +17,8 @@ defmodule MastaniServerWeb.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(:favorite_action, do: value(:favorite)) - enum(:cms_comment, do: value(:post_comment)) enum(:star_action, do: value(:star)) enum(:comment_action, do: value(:comment)) @@ -28,18 +27,58 @@ defmodule MastaniServerWeb.Schema.CMS.Misc do value(false) end - enum :cms_action do + enum :react_action do value(:favorite) value(:star) - value(:watch) + # value(:watch) + end + + enum :reactable_action do + value(:star) + # value(:favorite) + # value(:watch) + end + + enum :react_thread do + value(:post) + value(:job) + value(:video) + value(:repo) + end + + enum :cms_comment do + value(:post_comment) + value(:job_comment) + value(:video_comment) + value(:repo_comment) + end + + enum :commentable_thread do + value(:post) + value(:job) + value(:video) + value(:repo) end enum :cms_thread do value(:post) value(:job) + value(:user) value(:video) value(:repo) value(:wiki) + value(:cheatsheet) + # for home pages + value(:city) + value(:share) + value(:news) + end + + enum :cms_topic do + value(:index) + value(:city) + value(:share) + value(:news) end enum :order_enum do @@ -146,7 +185,6 @@ defmodule MastaniServerWeb.Schema.CMS.Misc do field(:sort, :sort_enum) field(:tag, :string, default_value: :all) field(:community, :string) - # @desc "Matching a name" # field(:order, :order_enum, default_value: :desc) @@ -154,15 +192,60 @@ defmodule MastaniServerWeb.Schema.CMS.Misc do # field(:tag, :string, default_value: :all) end + @desc "posts_filter doc" + input_object :paged_posts_filter do + @desc "limit of records (default 20), if first > 30, only return 30 at most" + pagination_args() + + field(:when, :when_enum) + field(:sort, :sort_enum) + field(:tag, :string, default_value: :all) + field(:community, :string) + field(:topic, :string) + end + + @doc """ + cms github repo contribotor + """ + input_object :repo_contributor_input do + field(:avatar, :string) + field(:html_url, :string) + field(:nickname, :string) + end + + @doc """ + cms github repo contribotor, detail version + """ + input_object :github_contributor_input do + field(:github_id, non_null(:string)) + field(:avatar, non_null(:string)) + field(:html_url, non_null(:string)) + field(:nickname, non_null(:string)) + field(:bio, :string) + field(:location, :string) + field(:company, :string) + end + + @doc """ + cms github repo lang + """ + input_object :repo_lang_input do + field(:name, :string) + field(:color, :string) + end + @doc """ only used for reaction result, like: favorite/star/watch ... """ interface :article do field(:id, :id) - field(:title, :string) + # field(:title, :string) resolve_type(fn %CMS.Post{}, _ -> :post + %CMS.Job{}, _ -> :job + %CMS.Video{}, _ -> :video + %CMS.Repo{}, _ -> :repo _, _ -> nil end) end diff --git a/lib/mastani_server_web/schema/cms/cms_queries.ex b/lib/mastani_server_web/schema/cms/cms_queries.ex index dc50484a0..6e432fe73 100644 --- a/lib/mastani_server_web/schema/cms/cms_queries.ex +++ b/lib/mastani_server_web/schema/cms/cms_queries.ex @@ -5,6 +5,7 @@ defmodule MastaniServerWeb.Schema.CMS.Queries do use Helper.GqlSchemaSuite object :cms_queries do + @desc "spec community info" field :community, :community do # arg(:id, non_null(:id)) arg(:id, :id) @@ -39,6 +40,14 @@ defmodule MastaniServerWeb.Schema.CMS.Queries do resolve(&R.CMS.community_editors/3) end + @desc "get community geo cities info" + field :community_geo_info, list_of(:geo_info) do + arg(:id, non_null(:id)) + arg(:raw, :id) + + resolve(&R.CMS.community_geo_info/3) + end + @desc "get all categories" field :paged_categories, :paged_categories do arg(:filter, :paged_filter) @@ -63,7 +72,7 @@ defmodule MastaniServerWeb.Schema.CMS.Queries do @desc "get paged posts" field :paged_posts, :paged_posts do - arg(:filter, non_null(:paged_article_filter)) + arg(:filter, non_null(:paged_posts_filter)) middleware(M.PageSizeProof) resolve(&R.CMS.paged_posts/3) @@ -97,6 +106,18 @@ defmodule MastaniServerWeb.Schema.CMS.Queries do resolve(&R.CMS.paged_repos/3) end + @desc "get wiki by community raw name" + field :wiki, non_null(:wiki) do + arg(:community, :string) + resolve(&R.CMS.wiki/3) + end + + @desc "get cheatsheet by community raw name" + field :cheatsheet, non_null(:cheatsheet) do + arg(:community, :string) + resolve(&R.CMS.cheatsheet/3) + end + @desc "get job by id" field :job, non_null(:job) do arg(:id, non_null(:id)) @@ -111,17 +132,19 @@ defmodule MastaniServerWeb.Schema.CMS.Queries do resolve(&R.CMS.paged_jobs/3) end - field :favorite_users, :paged_users do + @desc "get paged users of a reaction related to cms content" + field :reaction_users, :paged_users do arg(:id, non_null(:id)) - arg(:thread, :cms_thread, default_value: :post) - arg(:action, :favorite_action, default_value: :favorite) - arg(:filter, :paged_article_filter) + arg(:thread, :react_thread, default_value: :post) + arg(:action, non_null(:react_action)) + arg(:filter, non_null(:paged_filter)) middleware(M.PageSizeProof) resolve(&R.CMS.reaction_users/3) end # get all tags + @desc "get paged tags" field :paged_tags, :paged_tags do arg(:filter, non_null(:paged_filter)) @@ -144,6 +167,7 @@ defmodule MastaniServerWeb.Schema.CMS.Queries do arg(:community_id, :id) arg(:community, :string) arg(:thread, :cms_thread, default_value: :post) + arg(:topic, :string) resolve(&R.CMS.get_tags/3) end @@ -158,7 +182,18 @@ defmodule MastaniServerWeb.Schema.CMS.Queries do resolve(&R.CMS.paged_comments/3) end + @desc "get paged comments participators" + field :paged_comments_participators, :paged_users do + arg(:id, non_null(:id)) + arg(:thread, :cms_thread, default_value: :post) + arg(:filter, :paged_filter) + + middleware(M.PageSizeProof) + resolve(&R.CMS.paged_comments_participators/3) + end + # comments + # TODO: remove field :comments, :paged_comments do arg(:id, non_null(:id)) arg(:thread, :cms_thread, default_value: :post) diff --git a/lib/mastani_server_web/schema/cms/cms_types.ex b/lib/mastani_server_web/schema/cms/cms_types.ex index 716e4cf6a..14cf5faa1 100644 --- a/lib/mastani_server_web/schema/cms/cms_types.ex +++ b/lib/mastani_server_web/schema/cms/cms_types.ex @@ -17,83 +17,6 @@ defmodule MastaniServerWeb.Schema.CMS.Types do field(:id, :id) end - object :comment do - field(:id, :id) - field(:body, :string) - field(:floor, :integer) - field(:author, :user, resolve: dataloader(CMS, :author)) - - field :reply_to, :comment do - resolve(dataloader(CMS, :reply_to)) - end - - field :likes, list_of(:user) do - arg(:filter, :members_filter) - - middleware(M.PageSizeProof) - resolve(dataloader(CMS, :likes)) - end - - field :likes_count, :integer do - arg(:count, :count_type, default_value: :count) - - resolve(dataloader(CMS, :likes)) - middleware(M.ConvertToInt) - end - - field :viewer_has_liked, :boolean do - arg(:viewer_did, :viewer_did_type, default_value: :viewer_did) - - middleware(M.Authorize, :login) - # put current user into dataloader's args - middleware(M.PutCurrentUser) - resolve(dataloader(CMS, :likes)) - middleware(M.ViewerDidConvert) - end - - field :dislikes, list_of(:user) do - arg(:filter, :members_filter) - - middleware(M.PageSizeProof) - resolve(dataloader(CMS, :dislikes)) - end - - field :viewer_has_disliked, :boolean do - arg(:viewer_did, :viewer_did_type, default_value: :viewer_did) - - middleware(M.Authorize, :login) - # put current user into dataloader's args - middleware(M.PutCurrentUser) - resolve(dataloader(CMS, :dislikes)) - middleware(M.ViewerDidConvert) - end - - field :dislikes_count, :integer do - arg(:count, :count_type, default_value: :count) - - resolve(dataloader(CMS, :dislikes)) - middleware(M.ConvertToInt) - end - - field :replies, list_of(:comment) do - arg(:filter, :members_filter) - - middleware(M.ForceLoader) - middleware(M.PageSizeProof) - resolve(dataloader(CMS, :replies)) - end - - field :replies_count, :integer do - arg(:count, :count_type, default_value: :count) - - resolve(dataloader(CMS, :replies)) - middleware(M.ConvertToInt) - end - - field(:inserted_at, :datetime) - field(:updated_at, :datetime) - end - object :post do interface(:article) field(:id, :id) @@ -101,57 +24,30 @@ defmodule MastaniServerWeb.Schema.CMS.Types do field(:digest, :string) field(:length, :integer) field(:link_addr, :string) + field(:copy_right, :string) field(:body, :string) field(:views, :integer) - # TODO: remove + # NOTE: only meaningful in paged-xxx queries field(:pin, :boolean) field(:trash, :boolean) field(:tags, list_of(:tag), resolve: dataloader(CMS, :tags)) - field(:inserted_at, :datetime) - field(:updated_at, :datetime) field(:author, :user, resolve: dataloader(CMS, :author)) field(:communities, list_of(:community), resolve: dataloader(CMS, :communities)) + # field(:topic) field :comments, list_of(:comment) do arg(:filter, :members_filter) - # middleware(M.ForceLoader) middleware(M.PageSizeProof) resolve(dataloader(CMS, :comments)) end - field :comments_count, :integer do - arg(:count, :count_type, default_value: :count) - - resolve(dataloader(CMS, :comments)) - middleware(M.ConvertToInt) - end - - field :comments_participators, list_of(:user) do - arg(:filter, :members_filter) - arg(:unique, :unique_type, default_value: true) - - middleware(M.ForceLoader) - middleware(M.PageSizeProof) - resolve(dataloader(CMS, :comments)) - end - - field :comments_participators2, list_of(:user) do - arg(:filter, :members_filter) - arg(:unique, :unique_type, default_value: true) - - middleware(M.PageSizeProof) - - resolve(fn post, _args, %{context: %{loader: loader}} -> - loader - |> Dataloader.load(CMS, {:many, CMS.PostComment}, cp_users: post.id) - |> on_load(fn loader -> - {:ok, Dataloader.get(loader, CMS, {:many, CMS.PostComment}, cp_users: post.id)} - end) - end) - end + # comments_count + # comments_participators / paged + comments_counter_fields(:post) + @desc "totalCount of unique participator list of a the comments" field :comments_participators_count, :integer do resolve(fn post, _args, %{context: %{loader: loader}} -> loader @@ -162,63 +58,51 @@ defmodule MastaniServerWeb.Schema.CMS.Types do end) end - field :comments_participators_count_wired, :integer do - arg(:unique, :unique_type, default_value: true) - arg(:count, :count_type, default_value: :count) - - # middleware(M.ForceLoader) - resolve(dataloader(CMS, :comments)) - # middleware(M.CountLength) - end - - field :viewer_has_favorited, :boolean do - arg(:viewer_did, :viewer_did_type, default_value: :viewer_did) - - middleware(M.Authorize, :login) - # put current user into dataloader's args - middleware(M.PutCurrentUser) - resolve(dataloader(CMS, :favorites)) - middleware(M.ViewerDidConvert) - end - - field :viewer_has_starred, :boolean do - arg(:viewer_did, :viewer_did_type, default_value: :viewer_did) + has_viewed_field() + # fields for: favorite count, favorited_users, viewer_did_favorite.. + favorite_fields(:post) + star_fields(:post) - middleware(M.Authorize, :login) - middleware(M.PutCurrentUser) - resolve(dataloader(CMS, :stars)) - middleware(M.ViewerDidConvert) - end - - field :favorited_users, list_of(:user) do - arg(:filter, :members_filter) - - middleware(M.PageSizeProof) - resolve(dataloader(CMS, :favorites)) - end - - field :favorited_count, :integer do - arg(:count, :count_type, default_value: :count) - arg(:type, :post_thread, default_value: :post) - # middleware(M.SeeMe) - resolve(dataloader(CMS, :favorites)) - middleware(M.ConvertToInt) - end + timestamp_fields() + end - field :starred_count, :integer do - arg(:count, :count_type, default_value: :count) - arg(:type, :post_thread, default_value: :post) + object :job do + interface(:article) + field(:id, :id) + field(:title, :string) + field(:desc, :string) + field(:company, :string) + field(:company_logo, :string) + field(:company_link, :string) + field(:digest, :string) + field(:length, :integer) + field(:link_addr, :string) + field(:copy_right, :string) + field(:body, :string) + field(:views, :integer) - resolve(dataloader(CMS, :stars)) - middleware(M.ConvertToInt) - end + field(:pin, :boolean) + field(:trash, :boolean) - field :starred_users, list_of(:user) do - arg(:filter, :members_filter) + field(:author, :user, resolve: dataloader(CMS, :author)) + field(:tags, list_of(:tag), resolve: dataloader(CMS, :tags)) + field(:communities, list_of(:community), resolve: dataloader(CMS, :communities)) - middleware(M.PageSizeProof) - resolve(dataloader(CMS, :stars)) - end + field(:salary, :string) + field(:exp, :string) + field(:education, :string) + field(:field, :string) + field(:finance, :string) + field(:scale, :string) + + # comments_count + # comments_participators + comments_counter_fields(:job) + + has_viewed_field() + # fields for: favorite count, favorited_users, viewer_did_favorite.. + favorite_fields(:job) + timestamp_fields() end object :video do @@ -232,6 +116,7 @@ defmodule MastaniServerWeb.Schema.CMS.Types do field(:author, :user, resolve: dataloader(CMS, :author)) field(:source, :string) + field(:publish_at, :string) field(:link, :string) field(:original_author, :string) field(:original_author_link, :string) @@ -239,68 +124,108 @@ defmodule MastaniServerWeb.Schema.CMS.Types do field(:pin, :boolean) field(:trash, :boolean) - # TODO: remove - # field(:pin, :boolean) - # field(:trash, :boolean) - # field(:tags, list_of(:tag), resolve: dataloader(CMS, :tags)) + field(:tags, list_of(:tag), resolve: dataloader(CMS, :tags)) field(:communities, list_of(:community), resolve: dataloader(CMS, :communities)) - field(:inserted_at, :datetime) - field(:updated_at, :datetime) + + # comments_count + # comments_participators + comments_counter_fields(:video) + + has_viewed_field() + # fields for: favorite count, favorited_users, viewer_did_favorite.. + favorite_fields(:video) + star_fields(:video) + timestamp_fields() end object :repo do # interface(:article) field(:id, :id) - field(:repo_name, :string) + field(:title, :string) + field(:owner_name, :string) + field(:owner_url, :string) + field(:repo_url, :string) + field(:author, :user, resolve: dataloader(CMS, :author)) + field(:desc, :string) + field(:homepage_url, :string) field(:readme, :string) - field(:language, :string) - field(:author, :user, resolve: dataloader(CMS, :author)) - field(:repo_link, :string) - field(:producer, :string) - field(:producer_link, :integer) + field(:star_count, :integer) + field(:issues_count, :integer) + field(:prs_count, :integer) + field(:fork_count, :integer) + field(:watch_count, :integer) - field(:repo_star_count, :integer) - field(:repo_fork_count, :integer) - field(:repo_watch_count, :integer) - field(:views, :integer) + field(:primary_language, :repo_lang) + field(:license, :string) + field(:release_tag, :string) + field(:contributors, list_of(:repo_contributor)) + + field(:views, :integer) field(:pin, :boolean) field(:trash, :boolean) # TODO: remove # field(:pin, :boolean) # field(:trash, :boolean) - # field(:tags, list_of(:tag), resolve: dataloader(CMS, :tags)) + field(:last_sync, :datetime) + + field(:tags, list_of(:tag), resolve: dataloader(CMS, :tags)) field(:communities, list_of(:community), resolve: dataloader(CMS, :communities)) - field(:inserted_at, :datetime) - field(:updated_at, :datetime) + + has_viewed_field() + # comments_count + # comments_participators + comments_counter_fields(:repo) + # fields for: favorite count, favorited_users, viewer_did_favorite.. + favorite_fields(:repo) + + timestamp_fields() end - object :job do - interface(:article) - field(:id, :id) - field(:title, :string) - field(:desc, :string) - field(:company, :string) - field(:company_logo, :string) - field(:digest, :string) + object :repo_contributor do + field(:avatar, :string) + field(:html_url, :string) + field(:nickname, :string) + end + + object :repo_lang do + field(:name, :string) + field(:color, :string) + end + + object :github_contributor do + field(:avatar, :string) + field(:bio, :string) + field(:html_url, :string) + field(:nickname, :string) field(:location, :string) - field(:length, :integer) - field(:link_addr, :string) - field(:body, :string) + field(:company, :string) + end + + object :wiki do + field(:id, :id) + field(:readme, :string) + field(:contributors, list_of(:github_contributor)) + + field(:last_sync, :datetime) field(:views, :integer) - field(:pin, :boolean) - field(:trash, :boolean) + timestamp_fields() + end - field(:author, :user, resolve: dataloader(CMS, :author)) - field(:tags, list_of(:tag), resolve: dataloader(CMS, :tags)) - field(:communities, list_of(:community), resolve: dataloader(CMS, :communities)) - field(:inserted_at, :datetime) - field(:updated_at, :datetime) + object :cheatsheet do + field(:id, :id) + field(:readme, :string) + field(:contributors, list_of(:github_contributor)) + + field(:last_sync, :datetime) + field(:views, :integer) + + timestamp_fields() end object :thread do @@ -329,23 +254,21 @@ defmodule MastaniServerWeb.Schema.CMS.Types do field(:desc, :string) field(:raw, :string) field(:logo, :string) - field(:inserted_at, :datetime) - field(:updated_at, :datetime) field(:author, :user, resolve: dataloader(CMS, :author)) field(:threads, list_of(:thread), resolve: dataloader(CMS, :threads)) field(:categories, list_of(:category), resolve: dataloader(CMS, :categories)) - # Big thanks: https://elixirforum.com/t/grouping-error-in-absinthe-dadaloader/13671/2 - # see also: https://github.com/absinthe-graphql/dataloader/issues/25 - field :posts_count, :integer do - resolve(fn community, _args, %{context: %{loader: loader}} -> - loader - |> Dataloader.load(CMS, {:one, CMS.Post}, posts_count: community.id) - |> on_load(fn loader -> - {:ok, Dataloader.get(loader, CMS, {:one, CMS.Post}, posts_count: community.id)} - end) - end) - end + @doc "total count of post contents" + content_counts_field(:post, CMS.Post) + + @doc "total count of job contents" + content_counts_field(:job, CMS.Job) + + @doc "total count of video contents" + content_counts_field(:video, CMS.Video) + + @doc "total count of repo contents" + content_counts_field(:repo, CMS.Repo) field :subscribers, list_of(:user) do arg(:filter, :members_filter) @@ -391,6 +314,8 @@ defmodule MastaniServerWeb.Schema.CMS.Types do # TODO add complex here to warning N+1 problem resolve(&R.Statistics.list_contributes_digest/3) end + + timestamp_fields() end object :category do @@ -399,8 +324,14 @@ defmodule MastaniServerWeb.Schema.CMS.Types do field(:raw, :string) field(:author, :user, resolve: dataloader(CMS, :author)) field(:communities, list_of(:community), resolve: dataloader(CMS, :communities)) - field(:inserted_at, :datetime) - field(:updated_at, :datetime) + + timestamp_fields() + end + + object :topic do + field(:id, :id) + field(:title, :string) + field(:raw, :string) end object :tag do @@ -410,8 +341,33 @@ defmodule MastaniServerWeb.Schema.CMS.Types do field(:thread, :string) field(:author, :user, resolve: dataloader(CMS, :author)) field(:community, :community, resolve: dataloader(CMS, :community)) - field(:inserted_at, :datetime) - field(:updated_at, :datetime) + field(:topic, :topic, resolve: dataloader(CMS, :topic)) + + timestamp_fields() + end + + object :comment do + comments_fields() + end + + object :post_comment do + comments_fields() + field(:post, :post, resolve: dataloader(CMS, :post)) + end + + object :job_comment do + comments_fields() + field(:job, :job, resolve: dataloader(CMS, :job)) + end + + object :video_comment do + comments_fields() + field(:video, :video, resolve: dataloader(CMS, :video)) + end + + object :repo_comment do + comments_fields() + field(:repo, :repo, resolve: dataloader(CMS, :repo)) end object :paged_categories do @@ -444,6 +400,26 @@ defmodule MastaniServerWeb.Schema.CMS.Types do pagination_fields() end + object :paged_post_comments do + field(:entries, list_of(:post_comment)) + pagination_fields() + end + + object :paged_job_comments do + field(:entries, list_of(:job_comment)) + pagination_fields() + end + + object :paged_video_comments do + field(:entries, list_of(:video_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() diff --git a/lib/mastani_server_web/schema/cms/mutations/community.ex b/lib/mastani_server_web/schema/cms/mutations/community.ex index 5eee46611..f1b4ed5ec 100644 --- a/lib/mastani_server_web/schema/cms/mutations/community.ex +++ b/lib/mastani_server_web/schema/cms/mutations/community.ex @@ -80,7 +80,7 @@ defmodule MastaniServerWeb.Schema.CMS.Mutations.Community do @desc "create independent thread" field :create_thread, :thread do arg(:title, non_null(:string)) - arg(:raw, non_null(:cms_thread)) + arg(:raw, non_null(:string)) arg(:index, :integer, default_value: 0) middleware(M.Authorize, :login) @@ -134,6 +134,7 @@ defmodule MastaniServerWeb.Schema.CMS.Mutations.Community do arg(:color, non_null(:rainbow_color_enum)) arg(:community_id, non_null(:id)) arg(:thread, :cms_thread, default_value: :post) + arg(:topic, :string, default_value: "posts") middleware(M.Authorize, :login) middleware(M.PassportLoader, source: :community) @@ -170,5 +171,43 @@ defmodule MastaniServerWeb.Schema.CMS.Mutations.Community do resolve(&R.CMS.delete_tag/3) end + + @desc "sync github wiki" + field :sync_wiki, :wiki do + arg(:community_id, non_null(:id)) + arg(:readme, non_null(:string)) + arg(:last_sync, non_null(:datetime)) + + middleware(M.Authorize, :login) + resolve(&R.CMS.sync_wiki/3) + end + + @desc "add contributor to wiki " + field :add_wiki_contributor, :wiki do + arg(:id, non_null(:id)) + arg(:contributor, non_null(:github_contributor_input)) + + middleware(M.Authorize, :login) + resolve(&R.CMS.add_wiki_contributor/3) + end + + @desc "sync github cheatsheets " + field :sync_cheatsheet, :cheatsheet do + arg(:community_id, non_null(:id)) + arg(:readme, non_null(:string)) + arg(:last_sync, non_null(:datetime)) + + middleware(M.Authorize, :login) + resolve(&R.CMS.sync_cheatsheet/3) + end + + @desc "add contributor to cheatsheets" + field :add_cheatsheet_contributor, :cheatsheet do + arg(:id, non_null(:id)) + arg(:contributor, non_null(:github_contributor_input)) + + middleware(M.Authorize, :login) + resolve(&R.CMS.add_cheatsheet_contributor/3) + end end end diff --git a/lib/mastani_server_web/schema/cms/mutations/job.ex b/lib/mastani_server_web/schema/cms/mutations/job.ex index 0805389ea..79d3e2602 100644 --- a/lib/mastani_server_web/schema/cms/mutations/job.ex +++ b/lib/mastani_server_web/schema/cms/mutations/job.ex @@ -10,13 +10,22 @@ defmodule MastaniServerWeb.Schema.CMS.Mutations.Job do arg(:title, non_null(:string)) arg(:company, non_null(:string)) arg(:company_logo, non_null(:string)) - arg(:location, non_null(:string)) + arg(:company_link, :string) arg(:body, non_null(:string)) arg(:digest, non_null(:string)) arg(:length, non_null(:integer)) arg(:community_id, non_null(:id)) + + arg(:salary, non_null(:string)) + arg(:exp, non_null(:string)) + arg(:education, non_null(:string)) + arg(:finance, non_null(:string)) + arg(:scale, non_null(:string)) + arg(:field, non_null(:string)) + + arg(:desc, :string) arg(:link_addr, :string) - arg(:link_source, :string) + arg(:copy_right, :string) arg(:thread, :cms_thread, default_value: :job) arg(:tags, list_of(:ids)) @@ -92,6 +101,17 @@ defmodule MastaniServerWeb.Schema.CMS.Mutations.Job do arg(:title, :string) arg(:body, :string) arg(:digest, :string) + arg(:salary, :string) + arg(:copy_right, :string) + arg(:desc, :string) + arg(:link_addr, :string) + + arg(:exp, :string) + arg(:education, :string) + arg(:field, :string) + arg(:finance, :string) + arg(:scale, :string) + # ... middleware(M.Authorize, :login) diff --git a/lib/mastani_server_web/schema/cms/mutations/operation.ex b/lib/mastani_server_web/schema/cms/mutations/operation.ex index 06a888a9d..86ced2a1a 100644 --- a/lib/mastani_server_web/schema/cms/mutations/operation.ex +++ b/lib/mastani_server_web/schema/cms/mutations/operation.ex @@ -129,19 +129,21 @@ defmodule MastaniServerWeb.Schema.CMS.Mutations.Operation do resolve(&R.CMS.unset_community/3) end + @desc "react on a cms content, except favorite" field :reaction, :article do arg(:id, non_null(:id)) - arg(:thread, non_null(:cms_thread)) - arg(:action, non_null(:cms_action)) + arg(:thread, non_null(:react_thread)) + arg(:action, non_null(:reactable_action)) middleware(M.Authorize, :login) resolve(&R.CMS.reaction/3) end + @desc "undoreact on a cms content" field :undo_reaction, :article do arg(:id, non_null(:id)) - arg(:thread, non_null(:cms_thread)) - arg(:action, non_null(:cms_action)) + arg(:thread, non_null(:react_thread)) + arg(:action, non_null(:reactable_action)) middleware(M.Authorize, :login) resolve(&R.CMS.undo_reaction/3) diff --git a/lib/mastani_server_web/schema/cms/mutations/post.ex b/lib/mastani_server_web/schema/cms/mutations/post.ex index 221515b18..03d945c26 100644 --- a/lib/mastani_server_web/schema/cms/mutations/post.ex +++ b/lib/mastani_server_web/schema/cms/mutations/post.ex @@ -12,8 +12,10 @@ defmodule MastaniServerWeb.Schema.CMS.Mutations.Post do arg(:digest, non_null(:string)) arg(:length, non_null(:integer)) arg(:link_addr, :string) + arg(:copy_right, :string) arg(:community_id, non_null(:id)) arg(:thread, :cms_thread, default_value: :post) + arg(:topic, :string, default_value: "posts") arg(:tags, list_of(:ids)) middleware(M.Authorize, :login) @@ -25,8 +27,9 @@ defmodule MastaniServerWeb.Schema.CMS.Mutations.Post do @desc "pin a post" field :pin_post, :post do arg(:id, non_null(:id)) - arg(:thread, :post_thread, default_value: :post) arg(:community_id, non_null(:id)) + arg(:thread, :post_thread, default_value: :post) + arg(:topic, :string, default_value: "posts") middleware(M.Authorize, :login) middleware(M.PassportLoader, source: :community) @@ -39,6 +42,7 @@ defmodule MastaniServerWeb.Schema.CMS.Mutations.Post do arg(:id, non_null(:id)) arg(:thread, :post_thread, default_value: :post) arg(:community_id, non_null(:id)) + arg(:topic, :string, default_value: "posts") middleware(M.Authorize, :login) middleware(M.PassportLoader, source: :community) @@ -90,6 +94,7 @@ defmodule MastaniServerWeb.Schema.CMS.Mutations.Post do arg(:title, :string) arg(:body, :string) arg(:digest, :string) + arg(:copy_right, :string) middleware(M.Authorize, :login) middleware(M.PassportLoader, source: :post) diff --git a/lib/mastani_server_web/schema/cms/mutations/repo.ex b/lib/mastani_server_web/schema/cms/mutations/repo.ex index 7285ba85e..a8a4d8307 100644 --- a/lib/mastani_server_web/schema/cms/mutations/repo.ex +++ b/lib/mastani_server_web/schema/cms/mutations/repo.ex @@ -7,19 +7,34 @@ defmodule MastaniServerWeb.Schema.CMS.Mutations.Repo do object :cms_repo_mutations do @desc "create a user" field :create_repo, :repo do - arg(:repo_name, non_null(:string)) + arg(:title, non_null(:string)) + arg(:owner_name, non_null(:string)) + arg(:owner_url, non_null(:string)) + arg(:repo_url, non_null(:string)) + arg(:desc, non_null(:string)) + arg(:homepage_url, :string) arg(:readme, non_null(:string)) - arg(:language, non_null(:string)) - arg(:repo_link, non_null(:string)) - arg(:producer, non_null(:string)) - arg(:producer_link, non_null(:string)) - arg(:repo_star_count, non_null(:integer)) - arg(:repo_fork_count, non_null(:integer)) - arg(:repo_watch_count, non_null(:integer)) + arg(:star_count, non_null(:integer)) + arg(:issues_count, non_null(:integer)) + arg(:prs_count, non_null(:integer)) + arg(:fork_count, non_null(:integer)) + arg(:watch_count, non_null(:integer)) + + arg(:license, :string) + arg(:release_tag, :string) + + arg(:contributors, list_of(:repo_contributor_input)) + arg(:primary_language, non_null(:repo_lang_input)) + + arg(:community_id, non_null(:id)) + arg(:thread, :cms_thread, default_value: :repo) + arg(:tags, list_of(:ids)) middleware(M.Authorize, :login) + middleware(M.PublishThrottle) + resolve(&R.CMS.create_content/3) end diff --git a/lib/mastani_server_web/schema/statistics/statistics_queries.ex b/lib/mastani_server_web/schema/statistics/statistics_queries.ex index 2c04d9dfd..113b4e59b 100644 --- a/lib/mastani_server_web/schema/statistics/statistics_queries.ex +++ b/lib/mastani_server_web/schema/statistics/statistics_queries.ex @@ -11,5 +11,10 @@ defmodule MastaniServerWeb.Schema.Statistics.Queries do resolve(&R.Statistics.list_contributes/3) end + + @desc "list cities geo info" + field :cities_geo_info, :paged_geo_infos do + resolve(&R.Statistics.list_cities_geo_info/3) + end end end diff --git a/lib/mastani_server_web/schema/statistics/statistics_types.ex b/lib/mastani_server_web/schema/statistics/statistics_types.ex index 651081ca0..848be61b3 100644 --- a/lib/mastani_server_web/schema/statistics/statistics_types.ex +++ b/lib/mastani_server_web/schema/statistics/statistics_types.ex @@ -2,7 +2,7 @@ defmodule MastaniServerWeb.Schema.Statistics.Types do use Absinthe.Schema.Notation use Absinthe.Ecto, repo: MastaniServer.Repo - # import Absinthe.Resolution.Helpers + # import MastaniServerWeb.Schema.Utils.Helper # alias MastaniServer.Accounts diff --git a/lib/mastani_server_web/schema/utils/common_types.ex b/lib/mastani_server_web/schema/utils/common_types.ex index 03ad99d96..073a06905 100644 --- a/lib/mastani_server_web/schema/utils/common_types.ex +++ b/lib/mastani_server_web/schema/utils/common_types.ex @@ -16,11 +16,24 @@ defmodule MastaniServerWeb.Schema.Utils.CommonTypes do field(:done, :boolean) end + object :geo_info do + field(:city, :string) + field(:value, :integer) + field(:long, :float) + field(:lant, :float) + end + + object :paged_geo_infos do + field(:entries, list_of(:geo_info)) + pagination_fields() + end + input_object :ids do field(:id, :id) end input_object :common_paged_filter do pagination_args() + field(:sort, :comment_sort_enum, default_value: :desc_inserted) end end diff --git a/lib/mastani_server_web/schema/utils/helper.ex b/lib/mastani_server_web/schema/utils/helper.ex index d63cb25a8..088b46ce4 100644 --- a/lib/mastani_server_web/schema/utils/helper.ex +++ b/lib/mastani_server_web/schema/utils/helper.ex @@ -6,6 +6,13 @@ defmodule MastaniServerWeb.Schema.Utils.Helper do @page_size get_config(:general, :page_size) # @default_inner_page_size 5 + defmacro timestamp_fields do + quote do + field(:inserted_at, :datetime) + field(:updated_at, :datetime) + end + end + # see: https://github.com/absinthe-graphql/absinthe/issues/363 defmacro pagination_args do quote do @@ -39,4 +46,238 @@ defmodule MastaniServerWeb.Schema.Utils.Helper do field(:huaban, :string) end end + + import Absinthe.Resolution.Helpers, only: [dataloader: 2] + + alias MastaniServer.CMS + alias MastaniServerWeb.Middleware, as: M + alias MastaniServerWeb.Resolvers, as: R + + # Big thanks: https://elixirforum.com/t/grouping-error-in-absinthe-dadaloader/13671/2 + # see also: https://github.com/absinthe-graphql/dataloader/issues/25 + defmacro content_counts_field(thread, schema) do + quote do + field unquote(String.to_atom("#{to_string(thread)}s_count")), :integer do + resolve(fn community, _args, %{context: %{loader: loader}} -> + loader + |> Dataloader.load(CMS, {:one, unquote(schema)}, [ + {unquote(String.to_atom("#{to_string(thread)}s_count")), community.id} + ]) + |> on_load(fn loader -> + {:ok, + Dataloader.get(loader, CMS, {:one, unquote(schema)}, [ + {unquote(String.to_atom("#{to_string(thread)}s_count")), community.id} + ])} + end) + end) + end + end + end + + defmacro has_viewed_field do + quote do + # @desc "if user has viewed this content" + field :viewer_has_viewed, :boolean do + middleware(M.Authorize, :login) + middleware(M.PutCurrentUser) + + resolve(dataloader(CMS, :viewers)) + middleware(M.ViewerDidConvert) + end + 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) + field(:body, :string) + field(:floor, :integer) + field(:author, :user, resolve: dataloader(CMS, :author)) + + field :reply_to, :comment do + resolve(dataloader(CMS, :reply_to)) + end + + field :likes, list_of(:user) do + arg(:filter, :members_filter) + + middleware(M.PageSizeProof) + resolve(dataloader(CMS, :likes)) + end + + field :likes_count, :integer do + arg(:count, :count_type, default_value: :count) + + resolve(dataloader(CMS, :likes)) + middleware(M.ConvertToInt) + end + + field :viewer_has_liked, :boolean do + arg(:viewer_did, :viewer_did_type, default_value: :viewer_did) + + middleware(M.Authorize, :login) + # put current user into dataloader's args + middleware(M.PutCurrentUser) + resolve(dataloader(CMS, :likes)) + middleware(M.ViewerDidConvert) + end + + field :dislikes, list_of(:user) do + arg(:filter, :members_filter) + + middleware(M.PageSizeProof) + resolve(dataloader(CMS, :dislikes)) + end + + field :viewer_has_disliked, :boolean do + arg(:viewer_did, :viewer_did_type, default_value: :viewer_did) + + middleware(M.Authorize, :login) + # put current user into dataloader's args + middleware(M.PutCurrentUser) + resolve(dataloader(CMS, :dislikes)) + middleware(M.ViewerDidConvert) + end + + field :dislikes_count, :integer do + arg(:count, :count_type, default_value: :count) + + resolve(dataloader(CMS, :dislikes)) + middleware(M.ConvertToInt) + end + + field :replies, list_of(:comment) do + arg(:filter, :members_filter) + + middleware(M.ForceLoader) + middleware(M.PageSizeProof) + resolve(dataloader(CMS, :replies)) + end + + field :replies_count, :integer do + arg(:count, :count_type, default_value: :count) + + resolve(dataloader(CMS, :replies)) + middleware(M.ConvertToInt) + end + + timestamp_fields() + end + end + + defmacro comments_counter_fields(thread) do + quote do + # @dec "total comments of the post" + field :comments_count, :integer do + arg(:count, :count_type, default_value: :count) + + resolve(dataloader(CMS, :comments)) + middleware(M.ConvertToInt) + end + + # @desc "unique participator list of a the comments" + field :comments_participators, list_of(:user) do + arg(:filter, :members_filter) + arg(:unique, :unique_type, default_value: true) + + # middleware(M.ForceLoader) + middleware(M.PageSizeProof) + resolve(dataloader(CMS, :comments)) + middleware(M.CutParticipators) + end + + field(:paged_comments_participators, :paged_users) do + arg( + :thread, + unquote(String.to_atom("#{to_string(thread)}_thread")), + default_value: unquote(thread) + ) + + resolve(&R.CMS.paged_comments_participators/3) + end + end + end end diff --git a/mix.exs b/mix.exs index 02fa208de..a49432a46 100644 --- a/mix.exs +++ b/mix.exs @@ -51,15 +51,15 @@ defmodule MastaniServer.Mixfile do # Type `mix help deps` for examples and options. defp deps do [ - {:phoenix, "~> 1.3.2"}, + {:phoenix, "~> 1.3.4"}, {:phoenix_pubsub, "~> 1.1.0"}, - {:phoenix_ecto, "~> 3.3.0"}, - {:ecto, "~> 2.2.9"}, + {:phoenix_ecto, "~> 3.4.0"}, + {:ecto, "~> 2.2.11"}, {:postgrex, ">= 0.13.5"}, {:gettext, "~> 0.11"}, {:cowboy, "~> 1.0"}, # GraphQl tool - {:absinthe, "~> 1.4.12"}, + {:absinthe, "~> 1.4.13"}, {:absinthe_ecto, "~> 0.1.3"}, # Plug support for Absinthe # {:absinthe_plug, "~> 1.4.4"}, @@ -84,11 +84,12 @@ defmodule MastaniServer.Mixfile do {:pre_commit, "~> 0.3.4"}, {:inch_ex, "~> 1.0", only: [:dev, :test]}, {:short_maps, "~> 0.1.1"}, - {:jason, "~> 1.0"}, + {:jason, "~> 1.1.1"}, {:credo, "~> 0.10.0", only: [:dev, :test], runtime: false}, {:dialyxir, "~> 1.0.0-rc.2", only: [:dev, :mock], runtime: false}, {:excoveralls, "~> 0.8", only: :test}, - {:sentry, "~> 6.4"} + {:sentry, "~> 7.0"}, + {:recase, "~> 0.3.0"} ] end diff --git a/mix.lock b/mix.lock index c52c95ad3..20b757606 100644 --- a/mix.lock +++ b/mix.lock @@ -6,7 +6,7 @@ "argon2_elixir": {:hex, :argon2_elixir, "1.2.14", "0fc4bfbc1b7e459954987d3d2f3836befd72d63f3a355e3978f5005dd6e80816", [], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"}, "base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"}, "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"}, - "certifi": {:hex, :certifi, "2.3.1", "d0f424232390bf47d82da8478022301c561cf6445b5b5fb6a84d49a9e76d2639", [:rebar3], [{:parse_trans, "3.2.0", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, + "certifi": {:hex, :certifi, "2.4.2", "75424ff0f3baaccfd34b1214184b6ef616d89e420b258bb0a5ea7d7bc628f7f0", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm"}, "comeonin": {:hex, :comeonin, "4.0.3", "4e257dcb748ed1ca2651b7ba24fdbd1bd24efd12482accf8079141e3fda23a10", [:mix], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, repo: "hexpm", optional: true]}], "hexpm"}, "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"}, @@ -14,47 +14,49 @@ "corsica": {:hex, :corsica, "1.1.2", "5ad8b9dcbeeda4762d78a57c0c8c2f88e1eef8741508517c98cb79e0db1f107d", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm"}, - "credo": {:hex, :credo, "0.10.0", "66234a95effaf9067edb19fc5d0cd5c6b461ad841baac42467afed96c78e5e9e", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, - "dataloader": {:hex, :dataloader, "1.0.3", "3943b1b1ebe5ef59e88065cdbef9f1c9a9cb997fb90fed7ff4141b0b015eaa57", [:mix], [{:ecto, ">= 0.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"}, + "credo": {:hex, :credo, "0.10.1", "e85efaf2dd7054399083ab2c6a5199f6cb1805de1a5b00d9e8c5f07033407b1f", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, + "dataloader": {:hex, :dataloader, "1.0.4", "7c2345c53c9e5b61420013fc53c8463ba347a938b61f66677eb47d9c4a53ac5d", [:mix], [{:ecto, ">= 0.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"}, "db_connection": {:hex, :db_connection, "1.1.3", "89b30ca1ef0a3b469b1c779579590688561d586694a3ce8792985d4d7e575a61", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"}, "decimal": {:hex, :decimal, "1.5.0", "b0433a36d0e2430e3d50291b1c65f53c37d56f83665b43d79963684865beab68", [:mix], [], "hexpm"}, "dialyxir": {:hex, :dialyxir, "1.0.0-rc.3", "774306f84973fc3f1e2e8743eeaa5f5d29b117f3916e5de74c075c02f1b8ef55", [:mix], [], "hexpm"}, - "ecto": {:hex, :ecto, "2.2.10", "e7366dc82f48f8dd78fcbf3ab50985ceeb11cb3dc93435147c6e13f2cda0992e", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"}, + "ecto": {:hex, :ecto, "2.2.11", "4bb8f11718b72ba97a2696f65d247a379e739a0ecabf6a13ad1face79844791c", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"}, "elixir_make": {:hex, :elixir_make, "0.4.0", "992f38fabe705bb45821a728f20914c554b276838433349d4f2341f7a687cddf", [], [], "hexpm"}, "ex_unit_notifier": {:hex, :ex_unit_notifier, "0.1.4", "36a2dcab829f506e01bf17816590680dd1474407926d43e64c1263e627c364b8", [:mix], [], "hexpm"}, - "excoveralls": {:hex, :excoveralls, "0.9.2", "299ea4903be7cb2959af0f919d258af116736ca8d507f86c12ef2184698e21a0", [:mix], [{:hackney, ">= 0.12.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, + "excoveralls": {:hex, :excoveralls, "0.10.0", "a4508bdd408829f38e7b2519f234b7fd5c83846099cda348efcb5291b081200c", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, "faker": {:hex, :faker, "0.10.0", "367c2ae47e7b4ac6410e1eaa880c07b5fe4194476f697d37ac1ce25f3058aae2", [:mix], [], "hexpm"}, "file_system": {:hex, :file_system, "0.2.6", "fd4dc3af89b9ab1dc8ccbcc214a0e60c41f34be251d9307920748a14bf41f1d3", [:mix], [], "hexpm"}, "fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [:rebar], [], "hexpm"}, "gettext": {:hex, :gettext, "0.15.0", "40a2b8ce33a80ced7727e36768499fc9286881c43ebafccae6bab731e2b2b8ce", [:mix], [], "hexpm"}, - "guardian": {:hex, :guardian, "1.1.0", "36c1ea356a1bac02bc120c3f91f4f0259c5aa0ee92cee0efe8def5d7401f1921", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2 or ~> 1.3", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}, {:uuid, ">= 1.1.1", [hex: :uuid, repo: "hexpm", optional: false]}], "hexpm"}, - "hackney": {:hex, :hackney, "1.13.0", "24edc8cd2b28e1c652593833862435c80661834f6c9344e84b6a2255e7aeef03", [:rebar3], [{:certifi, "2.3.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.2", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, - "idna": {:hex, :idna, "5.1.2", "e21cb58a09f0228a9e0b95eaa1217f1bcfc31a1aaa6e1fdf2f53a33f7dbd9494", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, - "inch_ex": {:hex, :inch_ex, "1.0.0", "18496a900ca4b7542a1ff1159e7f8be6c2012b74ca55ac70de5e805f14cdf939", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, + "guardian": {:hex, :guardian, "1.1.1", "be14c4007eaf05268251ae114030cb7237ed9a9631c260022f020164ff4ed733", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2 or ~> 1.3", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, + "hackney": {:hex, :hackney, "1.14.3", "b5f6f5dcc4f1fba340762738759209e21914516df6be440d85772542d4a5e412", [:rebar3], [{:certifi, "2.4.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, + "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, + "inch_ex": {:hex, :inch_ex, "1.0.1", "1f0af1a83cec8e56f6fc91738a09c838e858db3d78ef5f2ec040fe4d5a62dabf", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, "jason": {:hex, :jason, "1.1.1", "d3ccb840dfb06f2f90a6d335b536dd074db748b3e7f5b11ab61d239506585eb2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, "jose": {:hex, :jose, "1.8.4", "7946d1e5c03a76ac9ef42a6e6a20001d35987afd68c2107bcd8f01a84e75aa73", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, "mime": {:hex, :mime, "1.3.0", "5e8d45a39e95c650900d03f897fbf99ae04f60ab1daa4a34c7a20a5151b7a5fe", [:mix], [], "hexpm"}, "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"}, - "mix_test_watch": {:hex, :mix_test_watch, "0.8.0", "acf97da2abc66532e7dc1aa66a5d6c9fc4442d7992d5d7eb4faeaeb964c2580e", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm"}, - "parse_trans": {:hex, :parse_trans, "3.2.0", "2adfa4daf80c14dc36f522cf190eb5c4ee3e28008fc6394397c16f62a26258c2", [:rebar3], [], "hexpm"}, + "mix_test_watch": {:hex, :mix_test_watch, "0.9.0", "c72132a6071261893518fa08e121e911c9358713f62794a90c95db59042af375", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm"}, + "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"}, "phoenix": {:hex, :phoenix, "1.3.4", "aaa1b55e5523083a877bcbe9886d9ee180bf2c8754905323493c2ac325903dc5", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, - "phoenix_ecto": {:hex, :phoenix_ecto, "3.3.0", "702f6e164512853d29f9d20763493f2b3bcfcb44f118af2bc37bb95d0801b480", [:mix], [{:ecto, "~> 2.1", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, - "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.0", "d55e25ff1ff8ea2f9964638366dfd6e361c52dedfd50019353598d11d4441d14", [:mix], [], "hexpm"}, - "plug": {:hex, :plug, "1.6.2", "e06a7bd2bb6de5145da0dd950070110dce88045351224bd98e84edfdaaf5ffee", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, + "phoenix_ecto": {:hex, :phoenix_ecto, "3.4.0", "91cd39427006fe4b5588d69f0941b9c3d3d8f5e6477c563a08379de7de2b0c58", [:mix], [{:ecto, "~> 2.1", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, + "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.1", "6668d787e602981f24f17a5fbb69cc98f8ab085114ebfac6cc36e10a90c8e93c", [:mix], [], "hexpm"}, + "plug": {:hex, :plug, "1.7.1", "8516d565fb84a6a8b2ca722e74e2cd25ca0fc9d64f364ec9dbec09d33eb78ccd", [], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}], "hexpm"}, + "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [], [], "hexpm"}, "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, "poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], [], "hexpm"}, "postgrex": {:hex, :postgrex, "0.13.5", "3d931aba29363e1443da167a4b12f06dcd171103c424de15e5f3fc2ba3e6d9c5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm"}, "pre_commit": {:hex, :pre_commit, "0.3.4", "e2850f80be8090d50ad8019ef2426039307ff5dfbe70c736ad0d4d401facf304", [:mix], [], "hexpm"}, "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"}, + "recase": {:hex, :recase, "0.3.0", "a3a6b2bfc9a1c3047b6f37d49ea52027ea59fd256505254b8e9d63c68d09ab89", [:mix], [], "hexpm"}, "scrivener": {:git, "https://github.com/mastani-stack/scrivener", "dc603c5cdf884c4fe33b2b09d5672ab6be3e2c14", []}, "scrivener_ecto": {:git, "https://github.com/mastani-stack/scrivener_ecto", "e7d2f287c9189f2aebf478724b6c276694411c92", []}, - "sentry": {:hex, :sentry, "6.4.1", "882287f1f3167dc4794865124977e2d88878d51d19930c0d0e7cc3a663a4a181", [:mix], [{:hackney, "~> 1.8 or 1.6.5", [hex: :hackney, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, + "sentry": {:hex, :sentry, "7.0.2", "0795e1e3955c9d1b9c35484dcba8169a76cff20cbc86de97327a4fe454515a85", [:mix], [{:hackney, "~> 1.8 or 1.6.5", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 1.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"}, "short_maps": {:hex, :short_maps, "0.1.2", "a7c2bfd91179cdbdfe90e74a023992335d116982fa672612c74776b2e9257a7b", [:mix], [], "hexpm"}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], [], "hexpm"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"}, "tesla": {:hex, :tesla, "0.10.0", "e588c7e7f1c0866c81eeed5c38f02a4a94d6309eede336c1e6ca08b0a95abd3f", [:mix], [{:exjsx, ">= 0.1.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"}, "timex": {:hex, :timex, "3.2.1", "639975eac45c4c08c2dbf7fc53033c313ff1f94fad9282af03619a3826493612", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"}, "tzdata": {:hex, :tzdata, "0.5.16", "13424d3afc76c68ff607f2df966c0ab4f3258859bbe3c979c9ed1606135e7352", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, - "unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, "uuid": {:hex, :uuid, "1.1.8", "e22fc04499de0de3ed1116b770c7737779f226ceefa0badb3592e64d5cfb4eb9", [:mix], [], "hexpm"}, } diff --git a/priv/mock/cms_community_seeds.exs b/priv/mock/cms_community_seeds.exs index 85d528a81..cf2bcb7da 100644 --- a/priv/mock/cms_community_seeds.exs +++ b/priv/mock/cms_community_seeds.exs @@ -1,4 +1,4 @@ -import MastaniServer.Factory +import MastaniServer.Support.Factory alias Helper.ORM alias MastaniServer.CMS diff --git a/priv/mock/cms_job_seeds.exs b/priv/mock/cms_job_seeds.exs index 8d74ff0fc..a8020b248 100644 --- a/priv/mock/cms_job_seeds.exs +++ b/priv/mock/cms_job_seeds.exs @@ -1,5 +1,5 @@ # -import MastaniServer.Factory +import MastaniServer.Support.Factory db_insert(:job) diff --git a/priv/mock/cms_post_seeds.exs b/priv/mock/cms_post_seeds.exs index 32960513f..7c2e54cb6 100644 --- a/priv/mock/cms_post_seeds.exs +++ b/priv/mock/cms_post_seeds.exs @@ -1,4 +1,4 @@ # doc .. -import MastaniServer.Factory +import MastaniServer.Support.Factory db_insert(:post) diff --git a/priv/mock/cms_repo_seeds.exs b/priv/mock/cms_repo_seeds.exs index e5b4f36c0..3c1c00721 100644 --- a/priv/mock/cms_repo_seeds.exs +++ b/priv/mock/cms_repo_seeds.exs @@ -1,3 +1,3 @@ -import MastaniServer.Factory +import MastaniServer.Support.Factory -db_insert_multi(:repo, 5) +db_insert_multi(:repo, 1) diff --git a/priv/mock/cms_video_seeds.exs b/priv/mock/cms_video_seeds.exs index db3179bff..790c5a0b9 100644 --- a/priv/mock/cms_video_seeds.exs +++ b/priv/mock/cms_video_seeds.exs @@ -1,3 +1,3 @@ -import MastaniServer.Factory +import MastaniServer.Support.Factory db_insert_multi(:video, 3) diff --git a/priv/mock/geo_seeds.exs b/priv/mock/geo_seeds.exs new file mode 100644 index 000000000..91b0a6bff --- /dev/null +++ b/priv/mock/geo_seeds.exs @@ -0,0 +1,3 @@ +import MastaniServer.Support.Factory + +insert_geo_data() diff --git a/priv/mock/post_comments_seeds.exs b/priv/mock/post_comments_seeds.exs index 52c0d7d78..89d6d9743 100644 --- a/priv/mock/post_comments_seeds.exs +++ b/priv/mock/post_comments_seeds.exs @@ -1,4 +1,4 @@ -import MastaniServer.Factory +import MastaniServer.Support.Factory alias MastaniServer.{CMS, Accounts} diff --git a/priv/mock/user_contributes_seeds.exs b/priv/mock/user_contributes_seeds.exs index 60e40f189..9de0ee473 100644 --- a/priv/mock/user_contributes_seeds.exs +++ b/priv/mock/user_contributes_seeds.exs @@ -1,6 +1,6 @@ # use /test/support/populater -import MastaniServer.Factory +import MastaniServer.Support.Factory default_user = %{ username: "mydearxym", diff --git a/priv/mock/user_seeds.exs b/priv/mock/user_seeds.exs index a8aee831b..0c4669d78 100644 --- a/priv/mock/user_seeds.exs +++ b/priv/mock/user_seeds.exs @@ -1,6 +1,6 @@ # use /test/support/populater -import MastaniServer.Factory +import MastaniServer.Support.Factory default_user = %{ username: "mydearxym", diff --git a/priv/repo/migrations/20180925231814_create_geo_info.exs b/priv/repo/migrations/20180925231814_create_geo_info.exs new file mode 100644 index 000000000..bad498186 --- /dev/null +++ b/priv/repo/migrations/20180925231814_create_geo_info.exs @@ -0,0 +1,16 @@ +defmodule MastaniServer.Repo.Migrations.CreateGeoInfo do + use Ecto.Migration + + def change do + create table(:geos) do + add(:city, :string) + add(:long, :float) + add(:lant, :float) + add(:value, :integer, default: 0) + + timestamps() + end + + create(unique_index(:geos, [:city])) + end +end diff --git a/priv/repo/migrations/20180926065313_add_geo_info_to_community.exs b/priv/repo/migrations/20180926065313_add_geo_info_to_community.exs new file mode 100644 index 000000000..7eccb2b83 --- /dev/null +++ b/priv/repo/migrations/20180926065313_add_geo_info_to_community.exs @@ -0,0 +1,10 @@ +defmodule MastaniServer.Repo.Migrations.AddGeoInfoToCommunity do + use Ecto.Migration + alias MastaniServer.Support.GeoData + + def change do + alter table(:communities) do + add(:geo_info, :map, default: %{data: GeoData.all()}) + end + end +end diff --git a/priv/repo/migrations/20180926070915_add_geo_city_to_users.exs b/priv/repo/migrations/20180926070915_add_geo_city_to_users.exs new file mode 100644 index 000000000..0577d2af0 --- /dev/null +++ b/priv/repo/migrations/20180926070915_add_geo_city_to_users.exs @@ -0,0 +1,11 @@ +defmodule MastaniServer.Repo.Migrations.AddGeoCityToUsers do + use Ecto.Migration + + def change do + alter table(:users) do + add(:geo_city, :string) + end + + create(index(:users, [:geo_city])) + end +end diff --git a/priv/repo/migrations/20180928135647_alter_repos.exs b/priv/repo/migrations/20180928135647_alter_repos.exs new file mode 100644 index 000000000..64dacac76 --- /dev/null +++ b/priv/repo/migrations/20180928135647_alter_repos.exs @@ -0,0 +1,36 @@ +defmodule MastaniServer.Repo.Migrations.AlterRepos do + use Ecto.Migration + + def change do + alter table(:cms_repos) do + add(:title, :string) + add(:owner_name, :string) + add(:owner_url, :string) + add(:repo_url, :string) + + add(:homepage_url, :string) + + add(:issuesCount, :integer) + add(:prsCount, :integer) + add(:forkCount, :integer) + add(:watchCount, :integer) + + add(:primary_language, :string) + add(:license, :string) + add(:releaseTag, :string) + + add(:contributors, :map) + + remove(:repo_name) + remove(:repo_link) + + remove(:language) + remove(:producer) + remove(:producer_link) + + remove(:repo_star_count) + remove(:repo_fork_count) + remove(:repo_watch_count) + end + end +end diff --git a/priv/repo/migrations/20180928141144_rename_repos_fiels.exs b/priv/repo/migrations/20180928141144_rename_repos_fiels.exs new file mode 100644 index 000000000..2b3a2a854 --- /dev/null +++ b/priv/repo/migrations/20180928141144_rename_repos_fiels.exs @@ -0,0 +1,12 @@ +defmodule MastaniServer.Repo.Migrations.RenameReposFiels do + use Ecto.Migration + + def change do + rename(table(:cms_repos), :issuesCount, to: :issues_count) + rename(table(:cms_repos), :prsCount, to: :prs_count) + rename(table(:cms_repos), :forkCount, to: :fork_count) + rename(table(:cms_repos), :watchCount, to: :watch_count) + + rename(table(:cms_repos), :releaseTag, to: :release_tag) + end +end diff --git a/priv/repo/migrations/20180929044158_add_star_language_info_to_repos.exs b/priv/repo/migrations/20180929044158_add_star_language_info_to_repos.exs new file mode 100644 index 000000000..2485df994 --- /dev/null +++ b/priv/repo/migrations/20180929044158_add_star_language_info_to_repos.exs @@ -0,0 +1,11 @@ +defmodule MastaniServer.Repo.Migrations.AddStarLanguageInfoToRepos do + use Ecto.Migration + + def change do + alter table(:cms_repos) do + add(:star_count, :integer) + remove(:primary_language) + add(:primary_language, :map) + end + end +end diff --git a/priv/repo/migrations/20180930005859_replace_string_to_text_in_post_comments.exs b/priv/repo/migrations/20180930005859_replace_string_to_text_in_post_comments.exs new file mode 100644 index 000000000..11f902993 --- /dev/null +++ b/priv/repo/migrations/20180930005859_replace_string_to_text_in_post_comments.exs @@ -0,0 +1,10 @@ +defmodule MastaniServer.Repo.Migrations.ReplaceStringToTextInPostComments do + use Ecto.Migration + + def change do + alter table(:posts_comments) do + remove(:body) + add(:body, :text) + end + end +end diff --git a/priv/repo/migrations/20180930010331_replace_string_to_text_in_job_comments.exs b/priv/repo/migrations/20180930010331_replace_string_to_text_in_job_comments.exs new file mode 100644 index 000000000..c55c1d325 --- /dev/null +++ b/priv/repo/migrations/20180930010331_replace_string_to_text_in_job_comments.exs @@ -0,0 +1,10 @@ +defmodule MastaniServer.Repo.Migrations.ReplaceStringToTextInJobComments do + use Ecto.Migration + + def change do + alter table(:jobs_comments) do + remove(:body) + add(:body, :text) + end + end +end diff --git a/priv/repo/migrations/20180930011202_create_video_comments.exs b/priv/repo/migrations/20180930011202_create_video_comments.exs new file mode 100644 index 000000000..85be724aa --- /dev/null +++ b/priv/repo/migrations/20180930011202_create_video_comments.exs @@ -0,0 +1,16 @@ +defmodule MastaniServer.Repo.Migrations.CreateVideoComments do + use Ecto.Migration + + def change do + create table(:videos_comments) do + add(:body, :text) + add(:author_id, references(:users, on_delete: :delete_all), null: false) + add(:video_id, references(:cms_videos, on_delete: :delete_all), null: false) + + timestamps() + end + + create(index(:videos_comments, [:author_id])) + create(index(:videos_comments, [:video_id])) + end +end diff --git a/priv/repo/migrations/20180930012820_create_video_comments_reply.exs b/priv/repo/migrations/20180930012820_create_video_comments_reply.exs new file mode 100644 index 000000000..d21fb5194 --- /dev/null +++ b/priv/repo/migrations/20180930012820_create_video_comments_reply.exs @@ -0,0 +1,15 @@ +defmodule MastaniServer.Repo.Migrations.CreateVideoCommentsReply do + use Ecto.Migration + + def change do + create table(:videos_comments_replies) do + add(:comment_id, references(:videos_comments, on_delete: :delete_all), null: false) + add(:reply_id, references(:videos_comments, on_delete: :delete_all), null: false) + + timestamps() + end + + create(index(:videos_comments_replies, [:comment_id])) + create(index(:videos_comments_replies, [:reply_id])) + end +end diff --git a/priv/repo/migrations/20180930014611_add_floor_to_video_comment.exs b/priv/repo/migrations/20180930014611_add_floor_to_video_comment.exs new file mode 100644 index 000000000..e4f2be474 --- /dev/null +++ b/priv/repo/migrations/20180930014611_add_floor_to_video_comment.exs @@ -0,0 +1,12 @@ +defmodule MastaniServer.Repo.Migrations.AddFloorToVideoComment do + use Ecto.Migration + + def change do + alter table(:videos_comments) do + add(:floor, :integer, default: 0) + end + + create(index(:videos_comments, [:floor])) + create(unique_index(:videos_comments, [:video_id, :author_id, :floor])) + end +end diff --git a/priv/repo/migrations/20180930021237_add_reply_to_video_comment.exs b/priv/repo/migrations/20180930021237_add_reply_to_video_comment.exs new file mode 100644 index 000000000..b5d6bfe9e --- /dev/null +++ b/priv/repo/migrations/20180930021237_add_reply_to_video_comment.exs @@ -0,0 +1,10 @@ +defmodule MastaniServer.Repo.Migrations.AddReplyToVideoComment do + use Ecto.Migration + + def change do + alter table(:videos_comments) do + # add(:author_id, references(:users, on_delete: :delete_all), null: false) + add(:reply_id, references(:videos_comments, on_delete: :delete_all)) + end + end +end diff --git a/priv/repo/migrations/20180930021707_drop_videos_comments_replies.exs b/priv/repo/migrations/20180930021707_drop_videos_comments_replies.exs new file mode 100644 index 000000000..1ea55ae25 --- /dev/null +++ b/priv/repo/migrations/20180930021707_drop_videos_comments_replies.exs @@ -0,0 +1,7 @@ +defmodule MastaniServer.Repo.Migrations.DropVideosCommentsReplies do + use Ecto.Migration + + def change do + drop(table(:videos_comments_replies)) + end +end diff --git a/priv/repo/migrations/20180930021722_recreate_videos_comments_replies.exs b/priv/repo/migrations/20180930021722_recreate_videos_comments_replies.exs new file mode 100644 index 000000000..79c46fef9 --- /dev/null +++ b/priv/repo/migrations/20180930021722_recreate_videos_comments_replies.exs @@ -0,0 +1,15 @@ +defmodule MastaniServer.Repo.Migrations.RecreateVideosCommentsReplies do + use Ecto.Migration + + def change do + create table(:videos_comments_replies) do + add(:video_comment_id, references(:videos_comments, on_delete: :delete_all), null: false) + add(:reply_id, references(:videos_comments, on_delete: :delete_all), null: false) + + timestamps() + end + + create(index(:videos_comments_replies, [:video_comment_id])) + create(index(:videos_comments_replies, [:reply_id])) + end +end diff --git a/priv/repo/migrations/20180930023126_create_likes_to_video_comment.exs b/priv/repo/migrations/20180930023126_create_likes_to_video_comment.exs new file mode 100644 index 000000000..47aeb9e5a --- /dev/null +++ b/priv/repo/migrations/20180930023126_create_likes_to_video_comment.exs @@ -0,0 +1,14 @@ +defmodule MastaniServer.Repo.Migrations.CreateLikesToVideoComment do + use Ecto.Migration + + def change do + create table(:videos_comments_likes) do + add(:user_id, references(:users, on_delete: :delete_all), null: false) + add(:video_comment_id, references(:videos_comments, on_delete: :delete_all), null: false) + + timestamps() + end + + create(unique_index(:videos_comments_likes, [:user_id, :video_comment_id])) + end +end diff --git a/priv/repo/migrations/20180930023322_create_dislikes_to_video_comment.exs b/priv/repo/migrations/20180930023322_create_dislikes_to_video_comment.exs new file mode 100644 index 000000000..fa42b6828 --- /dev/null +++ b/priv/repo/migrations/20180930023322_create_dislikes_to_video_comment.exs @@ -0,0 +1,14 @@ +defmodule MastaniServer.Repo.Migrations.CreateDislikesToVideoComment do + use Ecto.Migration + + def change do + create table(:videos_comments_dislikes) do + add(:user_id, references(:users, on_delete: :delete_all), null: false) + add(:video_comment_id, references(:videos_comments, on_delete: :delete_all), null: false) + + timestamps() + end + + create(unique_index(:videos_comments_dislikes, [:user_id, :video_comment_id])) + end +end diff --git a/priv/repo/migrations/20180930030608_create_repo_comment.exs b/priv/repo/migrations/20180930030608_create_repo_comment.exs new file mode 100644 index 000000000..6a959a996 --- /dev/null +++ b/priv/repo/migrations/20180930030608_create_repo_comment.exs @@ -0,0 +1,19 @@ +defmodule MastaniServer.Repo.Migrations.CreateRepoComment do + use Ecto.Migration + + def change do + create table(:repos_comments) do + add(:body, :text) + add(:floor, :integer, default: 0) + add(:author_id, references(:users, on_delete: :delete_all), null: false) + add(:repo_id, references(:cms_repos, on_delete: :delete_all), null: false) + # add(:author_id, references(:users, on_delete: :delete_all), null: false) + add(:reply_id, references(:repos_comments, on_delete: :delete_all)) + + timestamps() + end + + create(index(:repos_comments, [:author_id])) + create(index(:repos_comments, [:repo_id])) + end +end diff --git a/priv/repo/migrations/20180930031135_create_repo_comment_reply.exs b/priv/repo/migrations/20180930031135_create_repo_comment_reply.exs new file mode 100644 index 000000000..cc7d27256 --- /dev/null +++ b/priv/repo/migrations/20180930031135_create_repo_comment_reply.exs @@ -0,0 +1,15 @@ +defmodule MastaniServer.Repo.Migrations.CreateRepoCommentReply do + use Ecto.Migration + + def change do + create table(:repos_comments_replies) do + add(:repo_comment_id, references(:repos_comments, on_delete: :delete_all), null: false) + add(:reply_id, references(:repos_comments, on_delete: :delete_all), null: false) + + timestamps() + end + + create(index(:repos_comments_replies, [:repo_comment_id])) + create(index(:repos_comments_replies, [:reply_id])) + end +end diff --git a/priv/repo/migrations/20180930041418_create_repo_comment_likes.exs b/priv/repo/migrations/20180930041418_create_repo_comment_likes.exs new file mode 100644 index 000000000..2f3336637 --- /dev/null +++ b/priv/repo/migrations/20180930041418_create_repo_comment_likes.exs @@ -0,0 +1,14 @@ +defmodule MastaniServer.Repo.Migrations.CreateRepoCommentLikes do + use Ecto.Migration + + def change do + create table(:repos_comments_likes) do + add(:user_id, references(:users, on_delete: :delete_all), null: false) + add(:repo_comment_id, references(:repos_comments, on_delete: :delete_all), null: false) + + timestamps() + end + + create(unique_index(:repos_comments_likes, [:user_id, :repo_comment_id])) + end +end diff --git a/priv/repo/migrations/20180930041550_create_repo_comment_dislikes.exs b/priv/repo/migrations/20180930041550_create_repo_comment_dislikes.exs new file mode 100644 index 000000000..e5d81f0b3 --- /dev/null +++ b/priv/repo/migrations/20180930041550_create_repo_comment_dislikes.exs @@ -0,0 +1,14 @@ +defmodule MastaniServer.Repo.Migrations.CreateRepoCommentDislikes do + use Ecto.Migration + + def change do + create table(:repos_comments_dislikes) do + add(:user_id, references(:users, on_delete: :delete_all), null: false) + add(:repo_comment_id, references(:repos_comments, on_delete: :delete_all), null: false) + + timestamps() + end + + create(unique_index(:repos_comments_dislikes, [:user_id, :repo_comment_id])) + end +end diff --git a/priv/repo/migrations/20180930054149_create_community_wiki.exs b/priv/repo/migrations/20180930054149_create_community_wiki.exs new file mode 100644 index 000000000..afaeb2e38 --- /dev/null +++ b/priv/repo/migrations/20180930054149_create_community_wiki.exs @@ -0,0 +1,17 @@ +defmodule MastaniServer.Repo.Migrations.CreateCommunityWiki do + use Ecto.Migration + + def change do + create table(:community_wikis) do + add(:community_id, references(:communities, on_delete: :delete_all), null: false) + add(:readme, :text) + # this should be a embed schema + add(:contributors, :map) + add(:last_sync, :utc_datetime) + + timestamps() + end + + create(index(:community_wikis, [:community_id])) + end +end diff --git a/priv/repo/migrations/20180930131449_add_views_to_community_wiki.exs b/priv/repo/migrations/20180930131449_add_views_to_community_wiki.exs new file mode 100644 index 000000000..698c42929 --- /dev/null +++ b/priv/repo/migrations/20180930131449_add_views_to_community_wiki.exs @@ -0,0 +1,9 @@ +defmodule MastaniServer.Repo.Migrations.AddViewsToCommunityWiki do + use Ecto.Migration + + def change do + alter table(:community_wikis) do + add(:views, :integer, default: 0) + end + end +end diff --git a/priv/repo/migrations/20180930151113_create__community_cheatsheets.exs b/priv/repo/migrations/20180930151113_create__community_cheatsheets.exs new file mode 100644 index 000000000..1e069e773 --- /dev/null +++ b/priv/repo/migrations/20180930151113_create__community_cheatsheets.exs @@ -0,0 +1,18 @@ +defmodule MastaniServer.Repo.Migrations.CreateCommunityCheatsheets do + use Ecto.Migration + + def change do + create table(:community_cheatsheets) do + add(:community_id, references(:communities, on_delete: :delete_all), null: false) + add(:readme, :text) + # this should be a embed schema + add(:contributors, :map) + add(:last_sync, :utc_datetime) + add(:views, :integer, default: 0) + + timestamps() + end + + create(index(:community_cheatsheets, [:community_id])) + end +end diff --git a/priv/repo/migrations/20181007124749_create_videos_favorites.exs b/priv/repo/migrations/20181007124749_create_videos_favorites.exs new file mode 100644 index 000000000..b5136f353 --- /dev/null +++ b/priv/repo/migrations/20181007124749_create_videos_favorites.exs @@ -0,0 +1,14 @@ +defmodule MastaniServer.Repo.Migrations.CreateVideosFavorites do + use Ecto.Migration + + def change do + create table(:videos_favorites) do + add(:user_id, references(:users, on_delete: :delete_all), null: false) + add(:video_id, references(:cms_videos, on_delete: :delete_all), null: false) + + timestamps() + end + + create(unique_index(:videos_favorites, [:user_id, :video_id])) + end +end diff --git a/priv/repo/migrations/20181007125858_create_videos_stars.exs b/priv/repo/migrations/20181007125858_create_videos_stars.exs new file mode 100644 index 000000000..318f2e4ea --- /dev/null +++ b/priv/repo/migrations/20181007125858_create_videos_stars.exs @@ -0,0 +1,14 @@ +defmodule MastaniServer.Repo.Migrations.CreateVideosStars do + use Ecto.Migration + + def change do + create table(:videos_stars) do + add(:user_id, references(:users, on_delete: :delete_all), null: false) + add(:video_id, references(:cms_videos, on_delete: :delete_all), null: false) + + timestamps() + end + + create(unique_index(:videos_stars, [:user_id, :video_id])) + end +end diff --git a/priv/repo/migrations/20181008062314_add_likes_to_job_comments.exs b/priv/repo/migrations/20181008062314_add_likes_to_job_comments.exs new file mode 100644 index 000000000..d233d7f73 --- /dev/null +++ b/priv/repo/migrations/20181008062314_add_likes_to_job_comments.exs @@ -0,0 +1,14 @@ +defmodule MastaniServer.Repo.Migrations.AddLikesToJobComments do + use Ecto.Migration + + def change do + create table(:jobs_comments_likes) do + add(:user_id, references(:users, on_delete: :delete_all), null: false) + add(:job_comment_id, references(:jobs_comments, on_delete: :delete_all), null: false) + + timestamps() + end + + create(unique_index(:jobs_comments_likes, [:user_id, :job_comment_id])) + end +end diff --git a/priv/repo/migrations/20181008062922_add_dislikes_to_job_comments.exs b/priv/repo/migrations/20181008062922_add_dislikes_to_job_comments.exs new file mode 100644 index 000000000..369e5bfb4 --- /dev/null +++ b/priv/repo/migrations/20181008062922_add_dislikes_to_job_comments.exs @@ -0,0 +1,14 @@ +defmodule MastaniServer.Repo.Migrations.AddDislikesToJobComments do + use Ecto.Migration + + def change do + create table(:jobs_comments_dislikes) do + add(:user_id, references(:users, on_delete: :delete_all), null: false) + add(:job_comment_id, references(:jobs_comments, on_delete: :delete_all), null: false) + + timestamps() + end + + create(unique_index(:jobs_comments_dislikes, [:user_id, :job_comment_id])) + end +end diff --git a/priv/repo/migrations/20181011131417_add_category_id_to_jobs.exs b/priv/repo/migrations/20181011131417_add_category_id_to_jobs.exs new file mode 100644 index 000000000..37b055dc7 --- /dev/null +++ b/priv/repo/migrations/20181011131417_add_category_id_to_jobs.exs @@ -0,0 +1,11 @@ +defmodule MastaniServer.Repo.Migrations.AddCategoryIdToJobs do + use Ecto.Migration + + def change do + alter table(:jobs_favorites) do + add(:category_id, references(:favorite_categories, on_delete: :delete_all)) + end + + create(index(:jobs_favorites, [:category_id])) + end +end diff --git a/priv/repo/migrations/20181011133216_add_category_id_to_videos.exs b/priv/repo/migrations/20181011133216_add_category_id_to_videos.exs new file mode 100644 index 000000000..c7c1ac2fc --- /dev/null +++ b/priv/repo/migrations/20181011133216_add_category_id_to_videos.exs @@ -0,0 +1,11 @@ +defmodule MastaniServer.Repo.Migrations.AddCategoryIdToVideos do + use Ecto.Migration + + def change do + alter table(:videos_favorites) do + add(:category_id, references(:favorite_categories, on_delete: :delete_all)) + end + + create(index(:videos_favorites, [:category_id])) + end +end diff --git a/priv/repo/migrations/20181011133312_add_category_index_to_posts_favoritess.exs b/priv/repo/migrations/20181011133312_add_category_index_to_posts_favoritess.exs new file mode 100644 index 000000000..bf74953d1 --- /dev/null +++ b/priv/repo/migrations/20181011133312_add_category_index_to_posts_favoritess.exs @@ -0,0 +1,7 @@ +defmodule MastaniServer.Repo.Migrations.AddCategoryIndexToPostsFavoritess do + use Ecto.Migration + + def change do + create(index(:posts_favorites, [:category_id])) + end +end diff --git a/priv/repo/migrations/20181011152513_add_contribute_to_user_achievement.exs b/priv/repo/migrations/20181011152513_add_contribute_to_user_achievement.exs new file mode 100644 index 000000000..0ad010d3f --- /dev/null +++ b/priv/repo/migrations/20181011152513_add_contribute_to_user_achievement.exs @@ -0,0 +1,13 @@ +defmodule MastaniServer.Repo.Migrations.AddContributeToUserAchievement do + use Ecto.Migration + + def change do + alter table(:user_achievements) do + add( + :source_contribute, + :map, + default: %{web: false, server: false, mobile: false, we_app: false, h5: false} + ) + end + end +end diff --git a/priv/repo/migrations/20181012014834_add_last_updated_to_favorite_categories.exs b/priv/repo/migrations/20181012014834_add_last_updated_to_favorite_categories.exs new file mode 100644 index 000000000..ef75bfbf7 --- /dev/null +++ b/priv/repo/migrations/20181012014834_add_last_updated_to_favorite_categories.exs @@ -0,0 +1,9 @@ +defmodule MastaniServer.Repo.Migrations.AddLastUpdatedToFavoriteCategories do + use Ecto.Migration + + def change do + alter table(:favorite_categories) do + add(:last_updated, :utc_datetime) + end + end +end diff --git a/priv/repo/migrations/20181020014210_add_repo_favorites.exs b/priv/repo/migrations/20181020014210_add_repo_favorites.exs new file mode 100644 index 000000000..b5d0d6881 --- /dev/null +++ b/priv/repo/migrations/20181020014210_add_repo_favorites.exs @@ -0,0 +1,16 @@ +defmodule MastaniServer.Repo.Migrations.AddRepoFavorites do + use Ecto.Migration + + def change do + create table(:repos_favorites) do + add(:user_id, references(:users, on_delete: :delete_all), null: false) + add(:repo_id, references(:cms_repos, on_delete: :delete_all), null: false) + add(:category_id, references(:favorite_categories, on_delete: :delete_all)) + + timestamps() + end + + create(index(:repos_favorites, [:category_id])) + create(unique_index(:repos_favorites, [:user_id, :repo_id])) + end +end diff --git a/priv/repo/migrations/20181022060833_rename_repo_last_fetch.exs b/priv/repo/migrations/20181022060833_rename_repo_last_fetch.exs new file mode 100644 index 000000000..70ed066ba --- /dev/null +++ b/priv/repo/migrations/20181022060833_rename_repo_last_fetch.exs @@ -0,0 +1,7 @@ +defmodule MastaniServer.Repo.Migrations.RenameRepoLastFetch do + use Ecto.Migration + + def change do + rename(table(:cms_repos), :last_fetch_time, to: :last_sync) + end +end diff --git a/priv/repo/migrations/20181023004300_add_views_to_user.exs b/priv/repo/migrations/20181023004300_add_views_to_user.exs new file mode 100644 index 000000000..eba61c166 --- /dev/null +++ b/priv/repo/migrations/20181023004300_add_views_to_user.exs @@ -0,0 +1,9 @@ +defmodule MastaniServer.Repo.Migrations.AddViewsToUser do + use Ecto.Migration + + def change do + alter table(:users) do + add(:views, :integer, default: 0) + end + end +end diff --git a/priv/repo/migrations/20181023112702_add_copyright_to_posts.exs b/priv/repo/migrations/20181023112702_add_copyright_to_posts.exs new file mode 100644 index 000000000..4a4574e05 --- /dev/null +++ b/priv/repo/migrations/20181023112702_add_copyright_to_posts.exs @@ -0,0 +1,9 @@ +defmodule MastaniServer.Repo.Migrations.AddCopyrightToPosts do + use Ecto.Migration + + def change do + alter table(:cms_posts) do + add(:copy_right, :string, default: "original") + end + end +end diff --git a/priv/repo/migrations/20181023142819_create_posts_viewers.exs b/priv/repo/migrations/20181023142819_create_posts_viewers.exs new file mode 100644 index 000000000..a2cc93757 --- /dev/null +++ b/priv/repo/migrations/20181023142819_create_posts_viewers.exs @@ -0,0 +1,14 @@ +defmodule MastaniServer.Repo.Migrations.CreatePostsViewers do + use Ecto.Migration + + def change do + create table(:posts_viewers) do + add(:post_id, references(:cms_posts, on_delete: :delete_all), null: false) + add(:user_id, references(:users, on_delete: :delete_all), null: false) + + timestamps() + end + + create(unique_index(:posts_viewers, [:post_id, :user_id])) + end +end diff --git a/priv/repo/migrations/20181023153633_create_jobs_viewers.exs b/priv/repo/migrations/20181023153633_create_jobs_viewers.exs new file mode 100644 index 000000000..a15b39838 --- /dev/null +++ b/priv/repo/migrations/20181023153633_create_jobs_viewers.exs @@ -0,0 +1,14 @@ +defmodule MastaniServer.Repo.Migrations.CreateJobsViewers do + use Ecto.Migration + + def change do + create table(:jobs_viewers) do + add(:job_id, references(:cms_jobs, on_delete: :delete_all), null: false) + add(:user_id, references(:users, on_delete: :delete_all), null: false) + + timestamps() + end + + create(unique_index(:jobs_viewers, [:job_id, :user_id])) + end +end diff --git a/priv/repo/migrations/20181023154929_create_videos_viewers.exs b/priv/repo/migrations/20181023154929_create_videos_viewers.exs new file mode 100644 index 000000000..f1fd6bd3e --- /dev/null +++ b/priv/repo/migrations/20181023154929_create_videos_viewers.exs @@ -0,0 +1,14 @@ +defmodule MastaniServer.Repo.Migrations.CreateVideosViewers do + use Ecto.Migration + + def change do + create table(:videos_viewers) do + add(:video_id, references(:cms_videos, on_delete: :delete_all), null: false) + add(:user_id, references(:users, on_delete: :delete_all), null: false) + + timestamps() + end + + create(unique_index(:videos_viewers, [:video_id, :user_id])) + end +end diff --git a/priv/repo/migrations/20181023160033_create_repos_viewers.exs b/priv/repo/migrations/20181023160033_create_repos_viewers.exs new file mode 100644 index 000000000..b903513bf --- /dev/null +++ b/priv/repo/migrations/20181023160033_create_repos_viewers.exs @@ -0,0 +1,14 @@ +defmodule MastaniServer.Repo.Migrations.CreateReposViewers do + use Ecto.Migration + + def change do + create table(:repos_viewers) do + add(:repo_id, references(:cms_repos, on_delete: :delete_all), null: false) + add(:user_id, references(:users, on_delete: :delete_all), null: false) + + timestamps() + end + + create(unique_index(:repos_viewers, [:repo_id, :user_id])) + end +end diff --git a/priv/repo/migrations/20181024075229_add_company_link_to_jobs.exs b/priv/repo/migrations/20181024075229_add_company_link_to_jobs.exs new file mode 100644 index 000000000..7013141f8 --- /dev/null +++ b/priv/repo/migrations/20181024075229_add_company_link_to_jobs.exs @@ -0,0 +1,9 @@ +defmodule MastaniServer.Repo.Migrations.AddCompanyLinkToJobs do + use Ecto.Migration + + def change do + alter table(:cms_jobs) do + add(:company_link, :string) + end + end +end diff --git a/priv/repo/migrations/20181025060156_alter_job_fields.exs b/priv/repo/migrations/20181025060156_alter_job_fields.exs new file mode 100644 index 000000000..4cc18db71 --- /dev/null +++ b/priv/repo/migrations/20181025060156_alter_job_fields.exs @@ -0,0 +1,20 @@ +defmodule MastaniServer.Repo.Migrations.AlterJobFields do + use Ecto.Migration + + def change do + alter table(:cms_jobs) do + remove(:min_salary) + remove(:max_salary) + remove(:min_experience) + remove(:max_experience) + remove(:min_education) + remove(:link_source) + remove(:bonus) + + add(:salary, :string) + add(:exp, :string) + add(:education, :string) + add(:field, :string) + end + end +end diff --git a/priv/repo/migrations/20181025064950_add_fields_for_jobs.exs b/priv/repo/migrations/20181025064950_add_fields_for_jobs.exs new file mode 100644 index 000000000..d39110335 --- /dev/null +++ b/priv/repo/migrations/20181025064950_add_fields_for_jobs.exs @@ -0,0 +1,11 @@ +defmodule MastaniServer.Repo.Migrations.AddFieldsForJobs do + use Ecto.Migration + + def change do + alter table(:cms_jobs) do + add(:finance, :string) + add(:scale, :string) + add(:copy_right, :string) + end + end +end diff --git a/priv/repo/migrations/20181025130936_cleaup_for_jobs.exs b/priv/repo/migrations/20181025130936_cleaup_for_jobs.exs new file mode 100644 index 000000000..46c8dcefb --- /dev/null +++ b/priv/repo/migrations/20181025130936_cleaup_for_jobs.exs @@ -0,0 +1,12 @@ +defmodule MastaniServer.Repo.Migrations.CleaupForJobs do + use Ecto.Migration + + def change do + alter table(:cms_jobs) do + remove(:location) + remove(:copy_right) + + add(:copy_right, :string, default_value: "original") + end + end +end diff --git a/priv/repo/migrations/20181025131259_copy_right_default_for_jobs.exs b/priv/repo/migrations/20181025131259_copy_right_default_for_jobs.exs new file mode 100644 index 000000000..703425b0b --- /dev/null +++ b/priv/repo/migrations/20181025131259_copy_right_default_for_jobs.exs @@ -0,0 +1,11 @@ +defmodule MastaniServer.Repo.Migrations.CopyRightDefaultForJobs do + use Ecto.Migration + + def change do + alter table(:cms_jobs) do + remove(:copy_right) + + add(:copy_right, :string, default: "original") + end + end +end diff --git a/priv/repo/migrations/20181028010610_create_topic_for_contents.exs b/priv/repo/migrations/20181028010610_create_topic_for_contents.exs new file mode 100644 index 000000000..03b103d22 --- /dev/null +++ b/priv/repo/migrations/20181028010610_create_topic_for_contents.exs @@ -0,0 +1,17 @@ +defmodule MastaniServer.Repo.Migrations.CreateTopicForContents do + use Ecto.Migration + + def change do + create table(:topics) do + # add(:community_id, references(:communities, on_delete: :delete_all), null: false) + # add(:user_id, references(:users)) + add(:thread, :string) + add(:title, :string, default: "index") + add(:raw, :string, default: "index") + + timestamps() + end + + # create(unique_index(:topics, [:community, :part, :title])) + end +end diff --git a/priv/repo/migrations/20181028015325_create_topic_post_join.exs b/priv/repo/migrations/20181028015325_create_topic_post_join.exs new file mode 100644 index 000000000..363e607db --- /dev/null +++ b/priv/repo/migrations/20181028015325_create_topic_post_join.exs @@ -0,0 +1,12 @@ +defmodule MastaniServer.Repo.Migrations.CreateTopicPostJoin do + use Ecto.Migration + + def change do + create table(:posts_topics) do + add(:topic_id, references(:topics, on_delete: :delete_all), null: false) + add(:post_id, references(:cms_posts, on_delete: :delete_all), null: false) + end + + create(unique_index(:posts_topics, [:topic_id, :post_id])) + end +end diff --git a/priv/repo/migrations/20181028050903_add_topic_to_tags.exs b/priv/repo/migrations/20181028050903_add_topic_to_tags.exs new file mode 100644 index 000000000..645f26348 --- /dev/null +++ b/priv/repo/migrations/20181028050903_add_topic_to_tags.exs @@ -0,0 +1,13 @@ +defmodule MastaniServer.Repo.Migrations.AddTopicToTags do + use Ecto.Migration + + def change do + drop(unique_index(:tags, [:community_id, :thread, :title])) + + alter table(:tags) do + add(:topic_id, references(:topics, on_delete: :delete_all)) + end + + create(unique_index(:tags, [:community_id, :thread, :topic_id, :title])) + end +end diff --git a/priv/repo/migrations/20181102103138_add_flag_index_to_cms_contents.exs b/priv/repo/migrations/20181102103138_add_flag_index_to_cms_contents.exs new file mode 100644 index 000000000..c788796c3 --- /dev/null +++ b/priv/repo/migrations/20181102103138_add_flag_index_to_cms_contents.exs @@ -0,0 +1,10 @@ +defmodule MastaniServer.Repo.Migrations.AddFlagIndexToCmsContents do + use Ecto.Migration + + def change do + create(index(:posts_communities_flags, [:trash])) + create(index(:jobs_communities_flags, [:trash])) + create(index(:repos_communities_flags, [:trash])) + create(index(:videos_communities_flags, [:trash])) + end +end diff --git a/priv/repo/migrations/20181102103942_create_pined_posts.exs b/priv/repo/migrations/20181102103942_create_pined_posts.exs new file mode 100644 index 000000000..357ac158c --- /dev/null +++ b/priv/repo/migrations/20181102103942_create_pined_posts.exs @@ -0,0 +1,15 @@ +defmodule MastaniServer.Repo.Migrations.CreatePinedPosts do + use Ecto.Migration + + def change do + create table(:pined_posts) do + add(:post_id, references(:cms_posts, on_delete: :delete_all), null: false) + add(:community_id, references(:communities, on_delete: :delete_all), null: false) + add(:topic_id, references(:topics, on_delete: :delete_all), null: false) + + timestamps() + end + + create(unique_index(:pined_posts, [:post_id, :community_id, :topic_id])) + end +end diff --git a/priv/repo/migrations/20181102152301_create_pined_jobs.exs b/priv/repo/migrations/20181102152301_create_pined_jobs.exs new file mode 100644 index 000000000..e30a41d97 --- /dev/null +++ b/priv/repo/migrations/20181102152301_create_pined_jobs.exs @@ -0,0 +1,14 @@ +defmodule MastaniServer.Repo.Migrations.CreatePinedJobs do + use Ecto.Migration + + def change do + create table(:pined_jobs) do + add(:job_id, references(:cms_jobs, on_delete: :delete_all), null: false) + add(:community_id, references(:communities, on_delete: :delete_all), null: false) + + timestamps() + end + + create(unique_index(:pined_jobs, [:job_id, :community_id])) + end +end diff --git a/priv/repo/migrations/20181102164008_create_pined_videos.exs b/priv/repo/migrations/20181102164008_create_pined_videos.exs new file mode 100644 index 000000000..8314f33c1 --- /dev/null +++ b/priv/repo/migrations/20181102164008_create_pined_videos.exs @@ -0,0 +1,14 @@ +defmodule MastaniServer.Repo.Migrations.CreatePinedVideos do + use Ecto.Migration + + def change do + create table(:pined_videos) do + add(:video_id, references(:cms_videos, on_delete: :delete_all), null: false) + add(:community_id, references(:communities, on_delete: :delete_all), null: false) + + timestamps() + end + + create(unique_index(:pined_videos, [:video_id, :community_id])) + end +end diff --git a/priv/repo/migrations/20181102165813_create_pined_repos.exs b/priv/repo/migrations/20181102165813_create_pined_repos.exs new file mode 100644 index 000000000..29822cabb --- /dev/null +++ b/priv/repo/migrations/20181102165813_create_pined_repos.exs @@ -0,0 +1,14 @@ +defmodule MastaniServer.Repo.Migrations.CreatePinedRepos do + use Ecto.Migration + + def change do + create table(:pined_repos) do + add(:repo_id, references(:cms_repos, on_delete: :delete_all), null: false) + add(:community_id, references(:communities, on_delete: :delete_all), null: false) + + timestamps() + end + + create(unique_index(:pined_repos, [:repo_id, :community_id])) + end +end diff --git a/priv/repo/migrations/20181102174534_remove_pin_in_community_flags.exs b/priv/repo/migrations/20181102174534_remove_pin_in_community_flags.exs new file mode 100644 index 000000000..5a127b80e --- /dev/null +++ b/priv/repo/migrations/20181102174534_remove_pin_in_community_flags.exs @@ -0,0 +1,22 @@ +defmodule MastaniServer.Repo.Migrations.RemovePinInCommunityFlags do + use Ecto.Migration + + def change do + alter table(:posts_communities_flags) do + remove(:pin) + remove(:refined) + end + + alter table(:jobs_communities_flags) do + remove(:pin) + end + + alter table(:videos_communities_flags) do + remove(:pin) + end + + alter table(:repos_communities_flags) do + remove(:pin) + end + end +end diff --git a/priv/repo/migrations/20181103023157_add_more_customization.exs b/priv/repo/migrations/20181103023157_add_more_customization.exs new file mode 100644 index 000000000..13bce2ce2 --- /dev/null +++ b/priv/repo/migrations/20181103023157_add_more_customization.exs @@ -0,0 +1,13 @@ +defmodule MastaniServer.Repo.Migrations.AddMoreCustomization do + use Ecto.Migration + + def change do + alter table(:customizations) do + add(:banner_layout, :string) + add(:contents_layout, :string) + add(:content_divider, :boolean) + add(:mark_viewed, :boolean) + add(:display_density, :string) + end + end +end diff --git a/priv/repo/migrations/20181103031609_alter_customization_theme.exs b/priv/repo/migrations/20181103031609_alter_customization_theme.exs new file mode 100644 index 000000000..44fc64c27 --- /dev/null +++ b/priv/repo/migrations/20181103031609_alter_customization_theme.exs @@ -0,0 +1,10 @@ +defmodule MastaniServer.Repo.Migrations.AlterCustomizationTheme do + use Ecto.Migration + + def change do + alter table(:customizations) do + remove(:theme) + add(:theme, :string) + end + end +end diff --git a/test/helper/orm_test.exs b/test/helper/orm_test.exs index d2cc34480..fd84b84af 100644 --- a/test/helper/orm_test.exs +++ b/test/helper/orm_test.exs @@ -2,7 +2,7 @@ defmodule MastaniServer.Test.Helper.ORMTest do use MastaniServerWeb.ConnCase, async: true # TODO import Service.Utils move both helper and github - import MastaniServer.Factory + import MastaniServer.Support.Factory alias MastaniServer.CMS.{Post, Author} alias MastaniServer.Accounts.User diff --git a/test/mastani_server/accounts/accounts_test.exs b/test/mastani_server/accounts/accounts_test.exs index 40d8600f0..903d0761b 100644 --- a/test/mastani_server/accounts/accounts_test.exs +++ b/test/mastani_server/accounts/accounts_test.exs @@ -1,12 +1,10 @@ defmodule MastaniServer.Test.Accounts do use MastaniServer.TestTools - # TODO import Service.Utils move both helper and github import Helper.Utils - alias MastaniServer.Accounts - alias Helper.{Guardian, ORM} + alias MastaniServer.Accounts # @valid_user mock_attrs(:user) @valid_github_profile mock_attrs(:github_profile) |> map_key_stringify @@ -122,12 +120,47 @@ defmodule MastaniServer.Test.Accounts do assert g_user.node_id == @valid_github_profile["node_id"] end - test "exsit github user should not be created twice" do + test "new user have a empty achievement" do + assert {:error, _} = + ORM.find_by(GithubUser, github_id: to_string(@valid_github_profile["id"])) + + assert {:error, _} = ORM.find_by(User, nickname: @valid_github_profile["login"]) + + {:ok, %{token: token, user: _user}} = Accounts.github_signin(@valid_github_profile) + {:ok, claims, _info} = Guardian.jwt_decode(token) + + {:ok, created_user} = ORM.find(User, claims.id, preload: :achievement) + 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.source_contribute.h5 == false + assert achievement.source_contribute.web == false + assert achievement.source_contribute.we_app == false + assert achievement.source_contribute.mobile == false + end + + test "exsit github user created twice fails" do assert ORM.count(GithubUser) == 0 {:ok, _} = Accounts.github_signin(@valid_github_profile) assert ORM.count(GithubUser) == 1 {:ok, _} = Accounts.github_signin(@valid_github_profile) assert ORM.count(GithubUser) == 1 end + + test "github signin user should be locate geo city info" do + {:ok, guser} = Accounts.github_signin(@valid_github_profile) + {:ok, user} = ORM.find(User, guser.user.id) + + assert user.geo_city !== nil + end + + test "github signin user from invalid ip locate geo city fails" do + {:ok, guser} = Accounts.github_signin(@valid_github_profile, :fake_ip) + {:ok, user} = ORM.find(User, guser.user.id) + + assert user.geo_city == nil + end end end diff --git a/test/mastani_server/accounts/achievement_test.exs b/test/mastani_server/accounts/achievement_test.exs index 2ac990946..a2a487368 100644 --- a/test/mastani_server/accounts/achievement_test.exs +++ b/test/mastani_server/accounts/achievement_test.exs @@ -17,6 +17,37 @@ defmodule MastaniServer.Test.Accounts.Achievement do {:ok, ~m(user)a} end + alias MastaniServer.CMS + + describe "[Accounts Achievement communities]" do + test "normal user should have a empty editable communities list", ~m(user)a do + {:ok, results} = Accounts.list_editable_communities(user, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == 0 + end + + test "community editor should get a editable community list", ~m(user)a do + title = "chief editor" + {:ok, community} = db_insert(:community) + {:ok, community2} = db_insert(:community) + + {:ok, _} = CMS.set_editor(community, title, user) + {:ok, _} = CMS.set_editor(community2, title, user) + + # bad boy + {:ok, community_x} = db_insert(:community) + {:ok, user_x} = db_insert(:user) + {:ok, _} = CMS.set_editor(community_x, title, user_x) + + {:ok, editable_communities} = Accounts.list_editable_communities(user, %{page: 1, size: 20}) + + assert editable_communities.total_count == 2 + assert editable_communities.entries |> Enum.any?(&(&1.id == community.id)) + assert editable_communities.entries |> Enum.any?(&(&1.id == community2.id)) + end + end + describe "[Accounts Achievement funtion]" do alias Accounts.Achievement @@ -26,9 +57,9 @@ defmodule MastaniServer.Test.Accounts.Achievement do user |> Accounts.achieve(:add, :favorite) {:ok, achievement} = Achievement |> ORM.find_by(user_id: user.id) - assert achievement.followers_count == @follow_weight - assert achievement.contents_stared_count == @star_weight - assert achievement.contents_favorited_count == @favorite_weight + assert achievement.followers_count == 1 + assert achievement.contents_stared_count == 1 + assert achievement.contents_favorited_count == 1 reputation = @follow_weight + @star_weight + @favorite_weight assert achievement.reputation == reputation @@ -68,7 +99,7 @@ defmodule MastaniServer.Test.Accounts.Achievement do {:ok, user} = User |> ORM.find(user.id, preload: :achievement) - assert user.achievement.followers_count == @follow_weight * total_count + assert user.achievement.followers_count == total_count assert user.achievement.reputation == @follow_weight * total_count end @@ -85,8 +116,8 @@ defmodule MastaniServer.Test.Accounts.Achievement do {:ok, user} = User |> ORM.find(user.id, preload: :achievement) - assert user.achievement.followers_count == @follow_weight * (total_count - 1) - assert user.achievement.reputation == @follow_weight * (total_count - 1) + assert user.achievement.followers_count == total_count - 1 + assert user.achievement.reputation == @follow_weight * total_count - @follow_weight end end end diff --git a/test/mastani_server/accounts/customization_test.exs b/test/mastani_server/accounts/customization_test.exs index 3b72bd498..434929df6 100644 --- a/test/mastani_server/accounts/customization_test.exs +++ b/test/mastani_server/accounts/customization_test.exs @@ -13,36 +13,38 @@ defmodule MastaniServer.Test.Accounts.Customization do describe "[user customization]" do test "user can have default customization without payment", ~m(user)a do - {:ok, result} = Accounts.add_custom_setting(user, :theme, true) - assert result.theme == true + {:ok, result} = Accounts.set_customization(user, :banner_layout, "digest") + assert result.banner_layout == "digest" - {:ok, result} = Accounts.add_custom_setting(user, :theme) - assert result.theme == true - - {:error, _result} = Accounts.add_custom_setting(user, :non_exsit, true) + {:error, _result} = Accounts.set_customization(user, :non_exsit, true) end test "user set advance customization without payment fails", ~m(user)a do - {:error, _result} = Accounts.add_custom_setting(user, :non_exsit, true) - {:error, _result} = Accounts.add_custom_setting(user, :brainwash_free, true) + {:error, _result} = Accounts.set_customization(user, :non_exsit, true) + {:error, _result} = Accounts.set_customization(user, :brainwash_free, true) end test "user can set advance customization after pay for it", ~m(user)a do - {:error, _result} = Accounts.add_custom_setting(user, :brainwash_free, true) + {:error, _result} = Accounts.set_customization(user, :brainwash_free, true) {:ok, _result} = Accounts.purchase_service(user, :brainwash_free) - {:ok, _result} = Accounts.add_custom_setting(user, :brainwash_free, true) + {:ok, _result} = Accounts.set_customization(user, :brainwash_free, true) end test "user can set multiable customization at once", ~m(user)a do {:ok, result} = - Accounts.add_custom_setting(user, %{theme: true, sidebar_layout: %{hello: :world}}) + Accounts.set_customization(user, %{ + content_divider: true, + sidebar_layout: %{hello: :world} + }) - assert result.theme == true + assert result.content_divider == true assert result.sidebar_layout == %{hello: :world} - assert {:error, _result} = Accounts.add_custom_setting(user, %{theme: true, no_exsit: true}) - assert {:error, _result} = Accounts.add_custom_setting(user, %{}) + assert {:error, _result} = + Accounts.set_customization(user, %{content_divider: true, no_exsit: true}) + + assert {:error, _result} = Accounts.set_customization(user, %{}) end test "user can purchase multiable items at once", ~m(user)a do diff --git a/test/mastani_server/accounts/fans_test.exs b/test/mastani_server/accounts/fans_test.exs index 0dadf59d5..e9bbe67d0 100644 --- a/test/mastani_server/accounts/fans_test.exs +++ b/test/mastani_server/accounts/fans_test.exs @@ -17,9 +17,9 @@ defmodule MastaniServer.Test.Accounts.Fans do {:ok, user2} = db_insert(:user) {:ok, _followeer} = user |> Accounts.follow(user2) - {:ok, found} = User |> ORM.find(user.id, preload: :followers) + {:ok, found} = User |> ORM.find(user2.id, preload: :followers) - assert found |> Map.get(:followers) |> Enum.any?(&(&1.user_id == user.id)) + assert found |> Map.get(:followers) |> Enum.any?(&(&1.follower_id == user.id)) assert found |> Map.get(:followers) |> length == 1 end @@ -52,12 +52,12 @@ defmodule MastaniServer.Test.Accounts.Fans do {:ok, user2} = db_insert(:user) {:ok, _followeer} = user |> Accounts.follow(user2) - {:ok, found} = User |> ORM.find(user.id, preload: :followers) + {:ok, found} = User |> ORM.find(user2.id, preload: :followers) assert found |> Map.get(:followers) |> length == 1 {:ok, _followeer} = user |> Accounts.undo_follow(user2) - {:ok, found} = User |> ORM.find(user.id, preload: :followers) + {:ok, found} = User |> ORM.find(user2.id, preload: :followers) assert found |> Map.get(:followers) |> length == 0 end end diff --git a/test/mastani_server/accounts/favorite_category_test.exs b/test/mastani_server/accounts/favorite_category_test.exs index d04731220..2f7874135 100644 --- a/test/mastani_server/accounts/favorite_category_test.exs +++ b/test/mastani_server/accounts/favorite_category_test.exs @@ -134,6 +134,24 @@ defmodule MastaniServer.Test.Accounts.FavoriteCategory do 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 diff --git a/test/mastani_server/accounts/published_comments_test.exs b/test/mastani_server/accounts/published_comments_test.exs new file mode 100644 index 000000000..3d1c164a1 --- /dev/null +++ b/test/mastani_server/accounts/published_comments_test.exs @@ -0,0 +1,146 @@ +defmodule MastaniServer.Test.Accounts.PublishedComments do + use MastaniServer.TestTools + + alias MastaniServer.{Accounts, CMS} + + @publish_count 10 + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + + {:ok, ~m(user user2)a} + end + + describe "[Accounts Publised post comments]" do + test "fresh user get empty paged published posts", ~m(user)a do + {:ok, results} = Accounts.published_comments(user, :post, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == 0 + end + + test "user can get paged published posts", ~m(user user2)a do + body = "this is a test comment" + {:ok, post} = db_insert(:post) + {:ok, post2} = db_insert(:post) + + pub_comments = + Enum.reduce(1..@publish_count, [], fn _, acc -> + body = "this is a test comment" + {:ok, comment} = CMS.create_comment(:post, post.id, body, user) + acc ++ [comment] + end) + + {:ok, _comment} = CMS.create_comment(:post, post2.id, body, user) + {:ok, _comment} = CMS.create_comment(:post, post2.id, body, user2) + + {:ok, results} = Accounts.published_comments(user, :post, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == @publish_count + 1 + + random_comment_id = pub_comments |> Enum.random() |> Map.get(:id) + assert results.entries |> Enum.any?(&(&1.id == random_comment_id)) + end + end + + describe "[Accounts Publised job comments]" do + test "fresh user get empty paged published jobs", ~m(user)a do + {:ok, results} = Accounts.published_comments(user, :job, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == 0 + end + + test "user can get paged published jobs", ~m(user user2)a do + body = "this is a test comment" + {:ok, job} = db_insert(:job) + {:ok, job2} = db_insert(:job) + + pub_comments = + Enum.reduce(1..@publish_count, [], fn _, acc -> + body = "this is a test comment" + {:ok, comment} = CMS.create_comment(:job, job.id, body, user) + acc ++ [comment] + end) + + {:ok, _comment} = CMS.create_comment(:job, job2.id, body, user) + {:ok, _comment} = CMS.create_comment(:job, job2.id, body, user2) + + {:ok, results} = Accounts.published_comments(user, :job, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == @publish_count + 1 + + random_comment_id = pub_comments |> Enum.random() |> Map.get(:id) + assert results.entries |> Enum.any?(&(&1.id == random_comment_id)) + end + end + + describe "[Accounts Publised video comments]" do + test "fresh user get empty paged published videos", ~m(user)a do + {:ok, results} = Accounts.published_comments(user, :video, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == 0 + end + + test "user can get paged published videos", ~m(user user2)a do + body = "this is a test comment" + {:ok, video} = db_insert(:video) + {:ok, video2} = db_insert(:video) + + pub_comments = + Enum.reduce(1..@publish_count, [], fn _, acc -> + body = "this is a test comment" + {:ok, comment} = CMS.create_comment(:video, video.id, body, user) + acc ++ [comment] + end) + + {:ok, _comment} = CMS.create_comment(:video, video2.id, body, user) + {:ok, _comment} = CMS.create_comment(:video, video2.id, body, user2) + + {:ok, results} = Accounts.published_comments(user, :video, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == @publish_count + 1 + + random_comment_id = pub_comments |> Enum.random() |> Map.get(:id) + assert results.entries |> Enum.any?(&(&1.id == random_comment_id)) + end + end + + describe "[Accounts Publised repo comments]" do + test "fresh user get empty paged published repos", ~m(user)a do + {:ok, results} = Accounts.published_comments(user, :repo, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == 0 + end + + test "user can get paged published repos", ~m(user user2)a do + body = "this is a test comment" + {:ok, repo} = db_insert(:repo) + {:ok, repo2} = db_insert(:repo) + + pub_comments = + Enum.reduce(1..@publish_count, [], fn _, acc -> + body = "this is a test comment" + {:ok, comment} = CMS.create_comment(:repo, repo.id, body, user) + acc ++ [comment] + end) + + {:ok, _comment} = CMS.create_comment(:repo, repo2.id, body, user) + {:ok, _comment} = CMS.create_comment(:repo, repo2.id, body, user2) + + {:ok, results} = Accounts.published_comments(user, :repo, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == @publish_count + 1 + + random_comment_id = pub_comments |> Enum.random() |> Map.get(:id) + assert results.entries |> Enum.any?(&(&1.id == random_comment_id)) + end + end +end diff --git a/test/mastani_server/accounts/published_contents_test.exs b/test/mastani_server/accounts/published_contents_test.exs new file mode 100644 index 000000000..b1105a78c --- /dev/null +++ b/test/mastani_server/accounts/published_contents_test.exs @@ -0,0 +1,196 @@ +defmodule MastaniServer.Test.Accounts.PublishedContents do + use MastaniServer.TestTools + + alias MastaniServer.{Accounts, CMS} + + @publish_count 10 + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, community} = db_insert(:community) + {:ok, community2} = db_insert(:community) + + {:ok, ~m(user user2 community community2)a} + end + + describe "[Accounts Publised posts]" do + test "fresh user get empty paged published posts", ~m(user)a do + {:ok, results} = Accounts.published_contents(user, :post, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == 0 + end + + test "user can get paged published posts", ~m(user user2 community community2)a do + pub_posts = + Enum.reduce(1..@publish_count, [], fn _, acc -> + post_attrs = mock_attrs(:post, %{community_id: community.id}) + {:ok, post} = CMS.create_content(community, :post, post_attrs, user) + + acc ++ [post] + end) + + pub_posts2 = + Enum.reduce(1..@publish_count, [], fn _, acc -> + post_attrs = mock_attrs(:post, %{community_id: community2.id}) + {:ok, post} = CMS.create_content(community, :post, post_attrs, user) + + acc ++ [post] + end) + + # unrelated other user + Enum.reduce(1..5, [], fn _, acc -> + post_attrs = mock_attrs(:post, %{community_id: community.id}) + {:ok, post} = CMS.create_content(community, :post, post_attrs, user2) + + acc ++ [post] + end) + + {:ok, results} = Accounts.published_contents(user, :post, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == @publish_count * 2 + + random_post_id = pub_posts |> Enum.random() |> Map.get(:id) + random_post_id2 = pub_posts2 |> Enum.random() |> Map.get(:id) + assert results.entries |> Enum.any?(&(&1.id == random_post_id)) + assert results.entries |> Enum.any?(&(&1.id == random_post_id2)) + end + end + + describe "[Accounts Publised jobs]" do + test "fresh user get empty paged published jobs", ~m(user)a do + {:ok, results} = Accounts.published_contents(user, :job, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == 0 + end + + test "user can get paged published jobs", ~m(user user2 community community2)a do + pub_jobs = + Enum.reduce(1..@publish_count, [], fn _, acc -> + job_attrs = mock_attrs(:job, %{community_id: community.id}) + {:ok, job} = CMS.create_content(community, :job, job_attrs, user) + + acc ++ [job] + end) + + pub_jobs2 = + Enum.reduce(1..@publish_count, [], fn _, acc -> + job_attrs = mock_attrs(:job, %{community_id: community2.id}) + {:ok, job} = CMS.create_content(community, :job, job_attrs, user) + + acc ++ [job] + end) + + # unrelated other user + Enum.reduce(1..5, [], fn _, acc -> + job_attrs = mock_attrs(:job, %{community_id: community.id}) + {:ok, job} = CMS.create_content(community, :job, job_attrs, user2) + + acc ++ [job] + end) + + {:ok, results} = Accounts.published_contents(user, :job, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == @publish_count * 2 + + random_job_id = pub_jobs |> Enum.random() |> Map.get(:id) + random_job_id2 = pub_jobs2 |> Enum.random() |> Map.get(:id) + assert results.entries |> Enum.any?(&(&1.id == random_job_id)) + assert results.entries |> Enum.any?(&(&1.id == random_job_id2)) + end + end + + describe "[Accounts Publised videos]" do + test "fresh user get empty paged published videos", ~m(user)a do + {:ok, results} = Accounts.published_contents(user, :video, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == 0 + end + + test "user can get paged published videos", ~m(user user2 community community2)a do + pub_videos = + Enum.reduce(1..@publish_count, [], fn _, acc -> + video_attrs = mock_attrs(:video, %{community_id: community.id}) + {:ok, video} = CMS.create_content(community, :video, video_attrs, user) + + acc ++ [video] + end) + + pub_videos2 = + Enum.reduce(1..@publish_count, [], fn _, acc -> + video_attrs = mock_attrs(:video, %{community_id: community2.id}) + {:ok, video} = CMS.create_content(community, :video, video_attrs, user) + + acc ++ [video] + end) + + # unrelated other user + Enum.reduce(1..5, [], fn _, acc -> + video_attrs = mock_attrs(:video, %{community_id: community.id}) + {:ok, video} = CMS.create_content(community, :video, video_attrs, user2) + + acc ++ [video] + end) + + {:ok, results} = Accounts.published_contents(user, :video, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == @publish_count * 2 + + random_video_id = pub_videos |> Enum.random() |> Map.get(:id) + random_video_id2 = pub_videos2 |> Enum.random() |> Map.get(:id) + assert results.entries |> Enum.any?(&(&1.id == random_video_id)) + assert results.entries |> Enum.any?(&(&1.id == random_video_id2)) + end + end + + describe "[Accounts Publised repos]" do + test "fresh user get empty paged published repos", ~m(user)a do + {:ok, results} = Accounts.published_contents(user, :repo, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == 0 + end + + test "user can get paged published repos", ~m(user user2 community community2)a do + pub_repos = + Enum.reduce(1..@publish_count, [], fn _, acc -> + repo_attrs = mock_attrs(:repo, %{community_id: community.id}) + {:ok, repo} = CMS.create_content(community, :repo, repo_attrs, user) + + acc ++ [repo] + end) + + pub_repos2 = + Enum.reduce(1..@publish_count, [], fn _, acc -> + repo_attrs = mock_attrs(:repo, %{community_id: community2.id}) + {:ok, repo} = CMS.create_content(community, :repo, repo_attrs, user) + + acc ++ [repo] + end) + + # unrelated other user + Enum.reduce(1..5, [], fn _, acc -> + repo_attrs = mock_attrs(:repo, %{community_id: community.id}) + {:ok, repo} = CMS.create_content(community, :repo, repo_attrs, user2) + + acc ++ [repo] + end) + + {:ok, results} = Accounts.published_contents(user, :repo, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == @publish_count * 2 + + random_repo_id = pub_repos |> Enum.random() |> Map.get(:id) + random_repo_id2 = pub_repos2 |> Enum.random() |> Map.get(:id) + assert results.entries |> Enum.any?(&(&1.id == random_repo_id)) + assert results.entries |> Enum.any?(&(&1.id == random_repo_id2)) + end + end +end diff --git a/test/mastani_server/accounts/reacted_contents_test.exs b/test/mastani_server/accounts/reacted_contents_test.exs index a9b8a2dd8..72a855047 100644 --- a/test/mastani_server/accounts/reacted_contents_test.exs +++ b/test/mastani_server/accounts/reacted_contents_test.exs @@ -7,8 +7,9 @@ defmodule MastaniServer.Test.Accounts.ReactedContents do {:ok, user} = db_insert(:user) {:ok, post} = db_insert(:post) {:ok, job} = db_insert(:job) + {:ok, video} = db_insert(:video) - {:ok, ~m(user post job)a} + {:ok, ~m(user post job video)a} end describe "[user favorited contents]" do @@ -49,5 +50,14 @@ defmodule MastaniServer.Test.Accounts.ReactedContents do assert jobs |> is_valid_pagination?(:raw) assert job.id == jobs |> Map.get(:entries) |> List.first() |> Map.get(:id) end + + test "user can get paged stared_videos", ~m(user video)a do + {:ok, _} = CMS.reaction(:video, :star, video.id, user) + + filter = %{page: 1, size: 20} + {:ok, jobs} = Accounts.reacted_contents(:video, :star, filter, user) + assert jobs |> is_valid_pagination?(:raw) + assert video.id == jobs |> Map.get(:entries) |> List.first() |> Map.get(:id) + end end end diff --git a/test/mastani_server/cms/cheatsheet_test.exs b/test/mastani_server/cms/cheatsheet_test.exs new file mode 100644 index 000000000..b242eefeb --- /dev/null +++ b/test/mastani_server/cms/cheatsheet_test.exs @@ -0,0 +1,65 @@ +defmodule MastaniServer.Test.Cheatsheet do + use MastaniServer.TestTools + + alias Helper.ORM + alias MastaniServer.CMS + + alias CMS.{CommunityCheatsheet} + + setup do + {:ok, user} = db_insert(:user) + # {:ok, post} = db_insert(:post) + {:ok, community} = db_insert(:community) + + cheatsheet_attrs = mock_attrs(:cheatsheet, %{community_id: community.id}) + + {:ok, ~m(user community cheatsheet_attrs)a} + end + + describe "[cms cheatsheet sync]" do + test "can create create/sync a cheatsheet to a community", ~m(community cheatsheet_attrs)a do + {:ok, cheatsheet} = CMS.sync_github_content(community, :cheatsheet, cheatsheet_attrs) + + assert cheatsheet.community_id == community.id + assert cheatsheet.last_sync == cheatsheet_attrs.last_sync + end + + test "can update a exsit cheatsheet", ~m(community cheatsheet_attrs)a do + {:ok, cheatsheet} = CMS.sync_github_content(community, :cheatsheet, cheatsheet_attrs) + + new_cheatsheet_attrs = + mock_attrs(:cheatsheet, %{community_id: community.id, readme: "new readme"}) + + {:ok, _} = CMS.sync_github_content(community, :cheatsheet, new_cheatsheet_attrs) + {:ok, new_cheatsheet} = CommunityCheatsheet |> ORM.find(cheatsheet.id) + + assert new_cheatsheet.readme == "new readme" + end + + test "can add contributor to cheatsheet", ~m(community cheatsheet_attrs)a do + {:ok, cheatsheet} = CMS.sync_github_content(community, :cheatsheet, cheatsheet_attrs) + cur_contributors = cheatsheet.contributors + + contributor_attrs = mock_attrs(:github_contributor) + {:ok, cheatsheet} = CMS.add_contributor(cheatsheet, contributor_attrs) + update_contributors = cheatsheet.contributors + + assert length(update_contributors) == 1 + length(cur_contributors) + end + + test "add some contributor fails", ~m(community cheatsheet_attrs)a do + {:ok, cheatsheet} = CMS.sync_github_content(community, :cheatsheet, cheatsheet_attrs) + cur_contributors = cheatsheet.contributors + + contributor_attrs = mock_attrs(:github_contributor) + {:ok, cheatsheet} = CMS.add_contributor(cheatsheet, contributor_attrs) + update_contributors = cheatsheet.contributors + + assert length(update_contributors) == 1 + length(cur_contributors) + + # add some again + {:error, error} = CMS.add_contributor(cheatsheet, contributor_attrs) + assert error |> Keyword.get(:code) == ecode(:already_exsit) + end + end +end diff --git a/test/mastani_server/cms/cms_passport_test.exs b/test/mastani_server/cms/cms_passport_test.exs index 8da7847e0..d98dddab8 100644 --- a/test/mastani_server/cms/cms_passport_test.exs +++ b/test/mastani_server/cms/cms_passport_test.exs @@ -58,7 +58,7 @@ defmodule MastaniServer.Test.CMSPassport do end test "get a normal user's passport fails", ~m(user)a do - assert {:error, _} = CMS.get_passport(user) + assert {:ok, %{}} = CMS.get_passport(user) end test "get a non-exsit user's passport fails" do diff --git a/test/mastani_server/cms/content_flag_test.exs b/test/mastani_server/cms/content_flag_test.exs index 62e3cb481..aeec53df5 100644 --- a/test/mastani_server/cms/content_flag_test.exs +++ b/test/mastani_server/cms/content_flag_test.exs @@ -29,18 +29,16 @@ defmodule MastaniServer.Test.ContentFlags do end describe "[cms post flag]" do - test "user can set pin/trash flag on post based on community", ~m(community post)a do + test "user can set trash flag on post based on community", ~m(community post)a do community_id = community.id post_id = post.id {:ok, found} = PostCommunityFlag |> ORM.find_by(~m(post_id community_id)a) - assert found.pin == false assert found.trash == false - CMS.set_community_flags(%Post{id: post.id}, community.id, %{pin: true, trash: true}) + CMS.set_community_flags(%Post{id: post.id}, community.id, %{trash: true}) {:ok, found} = PostCommunityFlag |> ORM.find_by(~m(post_id community_id)a) - assert found.pin == true assert found.trash == true assert found.post_id == post.id assert found.community_id == community.id @@ -50,19 +48,17 @@ defmodule MastaniServer.Test.ContentFlags do end describe "[cms job flag]" do - test "user can set pin/trash flag on job", ~m(community job)a do + test "user can set trash flag on job", ~m(community job)a do community_id = community.id job_id = job.id {:ok, found} = JobCommunityFlag |> ORM.find_by(~m(job_id community_id)a) - assert found.pin == false assert found.trash == false - CMS.set_community_flags(%Job{id: job.id}, community.id, %{pin: true, trash: true}) + CMS.set_community_flags(%Job{id: job.id}, community.id, %{trash: true}) {:ok, found} = JobCommunityFlag |> ORM.find_by(~m(job_id community_id)a) - assert found.pin == true assert found.trash == true assert found.job_id == job.id assert found.community_id == community.id @@ -70,19 +66,17 @@ defmodule MastaniServer.Test.ContentFlags do end describe "[cms video flag]" do - test "user can set pin/trash flag on a video", ~m(community video)a do + test "user can set trash flag on a video", ~m(community video)a do community_id = community.id video_id = video.id {:ok, found} = VideoCommunityFlag |> ORM.find_by(~m(video_id community_id)a) - assert found.pin == false assert found.trash == false - CMS.set_community_flags(%Video{id: video.id}, community.id, %{pin: true, trash: true}) + CMS.set_community_flags(%Video{id: video.id}, community.id, %{trash: true}) {:ok, found} = VideoCommunityFlag |> ORM.find_by(~m(video_id community_id)a) - assert found.pin == true assert found.trash == true assert found.video_id == video.id assert found.community_id == community.id @@ -90,19 +84,17 @@ defmodule MastaniServer.Test.ContentFlags do end describe "[cms repo flag]" do - test "user can set pin/trash flag on repo", ~m(community repo)a do + test "user can set trash flag on repo", ~m(community repo)a do community_id = community.id repo_id = repo.id {:ok, found} = RepoCommunityFlag |> ORM.find_by(~m(repo_id community_id)a) - assert found.pin == false assert found.trash == false - CMS.set_community_flags(%Repo{id: repo.id}, community.id, %{pin: true, trash: true}) + CMS.set_community_flags(%Repo{id: repo.id}, community.id, %{trash: true}) {:ok, found} = RepoCommunityFlag |> ORM.find_by(~m(repo_id community_id)a) - assert found.pin == true assert found.trash == true assert found.repo_id == repo.id assert found.community_id == community.id diff --git a/test/mastani_server/cms/content_pin_test.exs b/test/mastani_server/cms/content_pin_test.exs new file mode 100644 index 000000000..7a0120d6b --- /dev/null +++ b/test/mastani_server/cms/content_pin_test.exs @@ -0,0 +1,89 @@ +defmodule MastaniServer.Test.ContentsPin do + use MastaniServer.TestTools + + # alias Helper.ORM + alias MastaniServer.CMS + + # alias CMS.{ + # Post, + # PinedPost, + # Job, + # PinedJob, + # } + + setup do + {:ok, user} = db_insert(:user) + {:ok, community} = db_insert(:community) + + {:ok, post} = CMS.create_content(community, :post, mock_attrs(:post), user) + {:ok, job} = CMS.create_content(community, :job, mock_attrs(:job), user) + {:ok, video} = CMS.create_content(community, :video, mock_attrs(:video), user) + {:ok, repo} = CMS.create_content(community, :repo, mock_attrs(:repo), user) + + {:ok, ~m(user community post job video repo)a} + end + + describe "[cms post pin]" do + test "can pin a post", ~m(community post)a do + {:ok, pined_post} = CMS.pin_content(post, community, "posts") + + assert pined_post.id == post.id + end + + test "can undo pin to a post", ~m(community post)a do + {:ok, pined_post} = CMS.pin_content(post, community, "posts") + assert pined_post.id == post.id + + assert {:ok, unpined} = CMS.undo_pin_content(post, community, "posts") + assert unpined.id == post.id + end + end + + describe "[cms job pin]" do + test "can pin a job", ~m(community job)a do + {:ok, pined_job} = CMS.pin_content(job, community) + + assert pined_job.id == job.id + end + + test "can undo pin to a job", ~m(community job)a do + {:ok, pined_job} = CMS.pin_content(job, community) + assert pined_job.id == job.id + + assert {:ok, unpined} = CMS.undo_pin_content(job, community) + assert unpined.id == job.id + end + end + + describe "[cms video pin]" do + test "can pin a video", ~m(community video)a do + {:ok, pined_video} = CMS.pin_content(video, community) + + assert pined_video.id == video.id + end + + test "can undo pin to a video", ~m(community video)a do + {:ok, pined_video} = CMS.pin_content(video, community) + assert pined_video.id == video.id + + assert {:ok, unpined} = CMS.undo_pin_content(video, community) + assert unpined.id == video.id + end + end + + describe "[cms repo pin]" do + test "can pin a repo", ~m(community repo)a do + {:ok, pined_repo} = CMS.pin_content(repo, community) + + assert pined_repo.id == repo.id + end + + test "can undo pin to a repo", ~m(community repo)a do + {:ok, pined_repo} = CMS.pin_content(repo, community) + assert pined_repo.id == repo.id + + assert {:ok, unpined} = CMS.undo_pin_content(repo, community) + assert unpined.id == repo.id + end + end +end diff --git a/test/mastani_server/cms/job_comment_test.exs b/test/mastani_server/cms/job_comment_test.exs index 489ed1c28..2839d9aa9 100644 --- a/test/mastani_server/cms/job_comment_test.exs +++ b/test/mastani_server/cms/job_comment_test.exs @@ -151,4 +151,60 @@ defmodule MastaniServer.Test.JobComment do 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(:job_comment, comment.id, user) + + {:ok, comment_preload} = ORM.find(JobComment, liked_comment.id, preload: :likes) + + assert comment_preload.likes |> Enum.any?(&(&1.job_comment_id == comment.id)) + end + + test "user like comment twice fails", ~m(comment user)a do + {:ok, _} = CMS.like_comment(:job_comment, comment.id, user) + {:error, _error} = CMS.like_comment(:job_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(:job_comment, comment.id, user) + {:ok, _} = CMS.undo_like_comment(:job_comment, comment.id, user) + + {:ok, comment_preload} = ORM.find(JobComment, comment.id, preload: :likes) + assert false == comment_preload.likes |> Enum.any?(&(&1.id == like.id)) + end + + test "user can dislike a comment", ~m(comment user)a do + {:ok, disliked_comment} = CMS.dislike_comment(:job_comment, comment.id, user) + + {:ok, comment_preload} = ORM.find(JobComment, disliked_comment.id, preload: :dislikes) + + assert comment_preload.dislikes |> Enum.any?(&(&1.job_comment_id == comment.id)) + end + + test "user can undo a dislike action", ~m(comment user)a do + {:ok, dislike} = CMS.dislike_comment(:job_comment, comment.id, user) + {:ok, _} = CMS.undo_dislike_comment(:job_comment, comment.id, user) + + {:ok, comment_preload} = ORM.find(JobComment, comment.id, preload: :dislikes) + assert false == comment_preload.dislikes |> Enum.any?(&(&1.id == dislike.id)) + end + + test "user can get paged likes of a job 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(:job_comment, comment.id, user1) + {:ok, _like2} = CMS.like_comment(:job_comment, comment.id, user2) + {:ok, _like3} = CMS.like_comment(:job_comment, comment.id, user3) + + {:ok, results} = CMS.reaction_users(:job_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/mastani_server/cms/repo_comment_test.exs b/test/mastani_server/cms/repo_comment_test.exs new file mode 100644 index 000000000..022c4f941 --- /dev/null +++ b/test/mastani_server/cms/repo_comment_test.exs @@ -0,0 +1,212 @@ +defmodule MastaniServer.Test.RepoComment do + # currently only test comments for repo type, rename and seprherate later + use MastaniServer.TestTools + + alias Helper.ORM + alias MastaniServer.CMS + + alias CMS.{RepoComment, RepoCommentReply} + + setup do + {:ok, repo} = db_insert(:repo) + {:ok, user} = db_insert(:user) + + body = "this is a test comment" + + {:ok, comment} = CMS.create_comment(:repo, repo.id, body, user) + + {:ok, ~m(repo user comment)a} + end + + describe "[comment CURD]" do + test "login user comment to exsiting repo", ~m(repo user)a do + body = "this is a test comment" + + assert {:ok, comment} = CMS.create_comment(:repo, repo.id, body, user) + + assert comment.repo_id == repo.id + assert comment.body == body + assert comment.author_id == user.id + end + + test "created comment should have a increased floor number", ~m(repo user)a do + body = "this is a test comment" + + assert {:ok, comment1} = CMS.create_comment(:repo, repo.id, body, user) + + {:ok, user2} = db_insert(:user) + + assert {:ok, comment2} = CMS.create_comment(:repo, repo.id, body, user2) + + assert comment1.floor == 2 + assert comment2.floor == 3 + end + + test "create comment to non-exsit repo fails", ~m(user)a do + body = "this is a test comment" + + assert {:error, _} = CMS.create_comment(:repo, non_exsit_id(), body, user) + end + + test "can reply a comment, and reply should be in comment replies list", ~m(comment user)a do + reply_body = "this is a reply comment" + + {:ok, reply} = CMS.reply_comment(:repo, comment.id, reply_body, user) + + {:ok, reply_preload} = ORM.find(RepoComment, reply.id, preload: :reply_to) + {:ok, comment_preload} = ORM.find(RepoComment, 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(repo user)a do + body = "this is a test comment" + + assert {:ok, comment} = CMS.create_comment(:repo, repo.id, body, user) + + {:ok, deleted} = CMS.delete_comment(:repo, comment.id) + assert deleted.id == comment.id + end + + # TODO may be a bug + @tag :bug + test "after delete, the coments of id > deleted.id should decrease the floor number", + ~m(repo 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(:repo, repo.id, 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(:repo, comment_1.id) + + {:ok, new_comment_2} = ORM.find(RepoComment, comment_2.id) + {:ok, new_comment_3} = ORM.find(RepoComment, comment_3.id) + {:ok, new_comment_last} = ORM.find(RepoComment, 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(comment user)a do + reply_body = "this is a reply comment" + + {:ok, reply} = CMS.reply_comment(:repo, comment.id, reply_body, user) + + RepoComment |> ORM.find_delete(comment.id) + + {:error, _} = ORM.find(RepoComment, comment.id) + {:error, _} = ORM.find(RepoComment, reply.id) + + {:error, _} = + RepoCommentReply |> ORM.find_by(repo_comment_id: comment.id, reply_id: reply.id) + end + + test "comments pagination should work", ~m(repo user)a do + body = "fake comment" + + Enum.reduce(1..30, [], fn _, acc -> + {:ok, value} = CMS.create_comment(:repo, repo.id, body, user) + + acc ++ [value] + end) + + {:ok, results} = CMS.list_comments(:repo, repo.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)a do + {:ok, user1} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, user3} = db_insert(:user) + + {:ok, _} = CMS.reply_comment(:repo, comment.id, "reply by user1", user1) + + {:ok, _} = CMS.reply_comment(:repo, comment.id, "reply by user2", user2) + + {:ok, _} = CMS.reply_comment(:repo, comment.id, "reply by user3", user3) + + {:ok, found_reply1} = CMS.list_replies(:repo, comment.id, user1) + assert user1.id == found_reply1 |> List.first() |> Map.get(:author_id) + + {:ok, found_reply2} = CMS.list_replies(:repo, comment.id, user2) + assert user2.id == found_reply2 |> List.first() |> Map.get(:author_id) + + {:ok, found_reply3} = CMS.list_replies(:repo, 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(:repo_comment, comment.id, user) + + {:ok, comment_preload} = ORM.find(RepoComment, liked_comment.id, preload: :likes) + + assert comment_preload.likes |> Enum.any?(&(&1.repo_comment_id == comment.id)) + end + + test "user like comment twice fails", ~m(comment user)a do + {:ok, _} = CMS.like_comment(:repo_comment, comment.id, user) + {:error, _error} = CMS.like_comment(:repo_comment, comment.id, user) + end + + test "user can undo a like action", ~m(comment user)a do + {:ok, like} = CMS.like_comment(:repo_comment, comment.id, user) + {:ok, _} = CMS.undo_like_comment(:repo_comment, comment.id, user) + + {:ok, comment_preload} = ORM.find(RepoComment, comment.id, preload: :likes) + assert false == comment_preload.likes |> Enum.any?(&(&1.id == like.id)) + end + + test "user can dislike a comment", ~m(comment user)a do + {:ok, disliked_comment} = CMS.dislike_comment(:repo_comment, comment.id, user) + + {:ok, comment_preload} = ORM.find(RepoComment, disliked_comment.id, preload: :dislikes) + + assert comment_preload.dislikes |> Enum.any?(&(&1.repo_comment_id == comment.id)) + end + + test "user can undo a dislike action", ~m(comment user)a do + {:ok, dislike} = CMS.dislike_comment(:repo_comment, comment.id, user) + {:ok, _} = CMS.undo_dislike_comment(:repo_comment, comment.id, user) + + {:ok, comment_preload} = ORM.find(RepoComment, comment.id, preload: :dislikes) + assert false == comment_preload.dislikes |> Enum.any?(&(&1.id == dislike.id)) + end + + test "user can get paged likes of a repo 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(:repo_comment, comment.id, user1) + {:ok, _like2} = CMS.like_comment(:repo_comment, comment.id, user2) + {:ok, _like3} = CMS.like_comment(:repo_comment, comment.id, user3) + + {:ok, results} = CMS.reaction_users(:repo_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/mastani_server/cms/repo_reactions_test.exs b/test/mastani_server/cms/repo_reactions_test.exs new file mode 100644 index 000000000..b5a2bad53 --- /dev/null +++ b/test/mastani_server/cms/repo_reactions_test.exs @@ -0,0 +1,32 @@ +defmodule MastaniServer.Test.RepoReactions do + use MastaniServer.TestTools + + alias MastaniServer.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/mastani_server/cms/repo_test.exs b/test/mastani_server/cms/repo_test.exs index c42e03a8a..fb568cc70 100644 --- a/test/mastani_server/cms/repo_test.exs +++ b/test/mastani_server/cms/repo_test.exs @@ -21,7 +21,9 @@ defmodule MastaniServer.Test.Repo do assert {:error, _} = ORM.find_by(Author, user_id: user.id) {:ok, repo} = CMS.create_content(community, :repo, repo_attrs, user) - assert repo.repo_name == repo_attrs.repo_name + + assert repo.title == repo_attrs.title + assert repo.contributors |> length !== 0 end test "add user to cms authors, if the user is not exsit in cms authors", diff --git a/test/mastani_server/cms/video_comment_test.exs b/test/mastani_server/cms/video_comment_test.exs new file mode 100644 index 000000000..15f690995 --- /dev/null +++ b/test/mastani_server/cms/video_comment_test.exs @@ -0,0 +1,217 @@ +defmodule MastaniServer.Test.VideoComment do + # currently only test comments for video type, rename and seprherate later + use MastaniServer.TestTools + + alias Helper.ORM + alias MastaniServer.CMS + + alias CMS.{VideoComment, VideoCommentReply} + + setup do + {:ok, video} = db_insert(:video) + {:ok, user} = db_insert(:user) + + body = "this is a test comment" + + {:ok, comment} = CMS.create_comment(:video, video.id, body, user) + + {:ok, ~m(video user comment)a} + end + + describe "[comment CURD]" do + test "login user comment to exsiting video", ~m(video user)a do + body = "this is a test comment" + + assert {:ok, comment} = CMS.create_comment(:video, video.id, body, user) + + assert comment.video_id == video.id + assert comment.body == body + assert comment.author_id == user.id + end + + test "created comment should have a increased floor number", ~m(video user)a do + body = "this is a test comment" + + assert {:ok, comment1} = CMS.create_comment(:video, video.id, body, user) + + {:ok, user2} = db_insert(:user) + + assert {:ok, comment2} = CMS.create_comment(:video, video.id, body, user2) + + assert comment1.floor == 2 + assert comment2.floor == 3 + end + + test "create comment to non-exsit video fails", ~m(user)a do + body = "this is a test comment" + + assert {:error, _} = CMS.create_comment(:video, non_exsit_id(), body, user) + end + + test "can reply a comment, and reply should be in comment replies list", ~m(comment user)a do + reply_body = "this is a reply comment" + + {:ok, reply} = CMS.reply_comment(:video, comment.id, reply_body, user) + + {:ok, reply_preload} = ORM.find(VideoComment, reply.id, preload: :reply_to) + {:ok, comment_preload} = ORM.find(VideoComment, 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(video user)a do + body = "this is a test comment" + + assert {:ok, comment} = CMS.create_comment(:video, video.id, body, user) + + {:ok, deleted} = CMS.delete_comment(:video, comment.id) + assert deleted.id == comment.id + end + + # TODO may be a bug + @tag :bug + # maybe case + # (Postgrex.Error) ERROR 23505 (unique_violation): duplicate key value violates unique constraint "videos_comments_video_id_author_id_floor_index" + # this will crash the server. + test "after delete, the coments of id > deleted.id should decrease the floor number", + ~m(video 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(:video, video.id, 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(:video, comment_1.id) + + {:ok, new_comment_2} = ORM.find(VideoComment, comment_2.id) + {:ok, new_comment_3} = ORM.find(VideoComment, comment_3.id) + {:ok, new_comment_last} = ORM.find(VideoComment, 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(comment user)a do + reply_body = "this is a reply comment" + + {:ok, reply} = CMS.reply_comment(:video, comment.id, reply_body, user) + + VideoComment |> ORM.find_delete(comment.id) + + {:error, _} = ORM.find(VideoComment, comment.id) + {:error, _} = ORM.find(VideoComment, reply.id) + + {:error, _} = + VideoCommentReply |> ORM.find_by(video_comment_id: comment.id, reply_id: reply.id) + end + + test "comments pagination should work", ~m(video user)a do + body = "fake comment" + + Enum.reduce(1..30, [], fn _, acc -> + {:ok, value} = CMS.create_comment(:video, video.id, body, user) + + acc ++ [value] + end) + + {:ok, results} = CMS.list_comments(:video, video.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)a do + {:ok, user1} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, user3} = db_insert(:user) + + {:ok, _} = CMS.reply_comment(:video, comment.id, "reply by user1", user1) + + {:ok, _} = CMS.reply_comment(:video, comment.id, "reply by user2", user2) + + {:ok, _} = CMS.reply_comment(:video, comment.id, "reply by user3", user3) + + {:ok, found_reply1} = CMS.list_replies(:video, comment.id, user1) + assert user1.id == found_reply1 |> List.first() |> Map.get(:author_id) + + {:ok, found_reply2} = CMS.list_replies(:video, comment.id, user2) + assert user2.id == found_reply2 |> List.first() |> Map.get(:author_id) + + {:ok, found_reply3} = CMS.list_replies(:video, 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(:video_comment, comment.id, user) + + {:ok, comment_preload} = ORM.find(VideoComment, liked_comment.id, preload: :likes) + + assert comment_preload.likes |> Enum.any?(&(&1.video_comment_id == comment.id)) + end + + test "user like comment twice fails", ~m(comment user)a do + {:ok, _} = CMS.like_comment(:video_comment, comment.id, user) + {:error, _error} = CMS.like_comment(:video_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(:video_comment, comment.id, user) + {:ok, _} = CMS.undo_like_comment(:video_comment, comment.id, user) + + {:ok, comment_preload} = ORM.find(VideoComment, comment.id, preload: :likes) + assert false == comment_preload.likes |> Enum.any?(&(&1.id == like.id)) + end + + test "user can dislike a comment", ~m(comment user)a do + # {:ok, like} = CMS.reaction(:video_comment, :like, comment.id, user.id) + {:ok, disliked_comment} = CMS.dislike_comment(:video_comment, comment.id, user) + + {:ok, comment_preload} = ORM.find(VideoComment, disliked_comment.id, preload: :dislikes) + + assert comment_preload.dislikes |> Enum.any?(&(&1.video_comment_id == comment.id)) + end + + test "user can undo a dislike action", ~m(comment user)a do + {:ok, dislike} = CMS.dislike_comment(:video_comment, comment.id, user) + {:ok, _} = CMS.undo_dislike_comment(:video_comment, comment.id, user) + + {:ok, comment_preload} = ORM.find(VideoComment, comment.id, preload: :dislikes) + assert false == comment_preload.dislikes |> Enum.any?(&(&1.id == dislike.id)) + end + + test "user can get paged likes of a video 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(:video_comment, comment.id, user1) + {:ok, _like2} = CMS.like_comment(:video_comment, comment.id, user2) + {:ok, _like3} = CMS.like_comment(:video_comment, comment.id, user3) + + {:ok, results} = CMS.reaction_users(:video_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/mastani_server/cms/video_reactions_test.exs b/test/mastani_server/cms/video_reactions_test.exs new file mode 100644 index 000000000..6c746350f --- /dev/null +++ b/test/mastani_server/cms/video_reactions_test.exs @@ -0,0 +1,49 @@ +defmodule MastaniServer.Test.VideoReactions do + use MastaniServer.TestTools + + alias MastaniServer.CMS + + setup do + {:ok, user} = db_insert(:user) + {:ok, community} = db_insert(:community) + + video_attrs = mock_attrs(:video, %{community_id: community.id}) + + {:ok, ~m(user community video_attrs)a} + end + + describe "[cms video star/favorite reaction]" do + test "star and undo star reaction to video", ~m(user community video_attrs)a do + {:ok, video} = CMS.create_content(community, :video, video_attrs, user) + + {:ok, _} = CMS.reaction(:video, :star, video.id, user) + {:ok, reaction_users} = CMS.reaction_users(:video, :star, video.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(:video, :star, video.id, user) + {:ok, reaction_users2} = CMS.reaction_users(:video, :star, video.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 video", ~m(user community video_attrs)a do + {:ok, video} = CMS.create_content(community, :video, video_attrs, user) + + {:ok, _} = CMS.reaction(:video, :favorite, video.id, user) + {:ok, reaction_users} = CMS.reaction_users(:video, :favorite, video.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(:video, :favorite, video.id, user) + + {:ok, reaction_users2} = + CMS.reaction_users(:video, :favorite, video.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/mastani_server/cms/wiki_test.exs b/test/mastani_server/cms/wiki_test.exs new file mode 100644 index 000000000..43bf10871 --- /dev/null +++ b/test/mastani_server/cms/wiki_test.exs @@ -0,0 +1,63 @@ +defmodule MastaniServer.Test.Wiki do + use MastaniServer.TestTools + + alias Helper.ORM + alias MastaniServer.CMS + + alias CMS.{CommunityWiki} + + setup do + {:ok, user} = db_insert(:user) + # {:ok, post} = db_insert(:post) + {:ok, community} = db_insert(:community) + + wiki_attrs = mock_attrs(:wiki, %{community_id: community.id}) + + {:ok, ~m(user community wiki_attrs)a} + end + + describe "[cms wiki sync]" do + test "can create create/sync a wiki to a community", ~m(community wiki_attrs)a do + {:ok, wiki} = CMS.sync_github_content(community, :wiki, wiki_attrs) + + assert wiki.community_id == community.id + assert wiki.last_sync == wiki_attrs.last_sync + end + + test "can update a exsit wiki", ~m(community wiki_attrs)a do + {:ok, wiki} = CMS.sync_github_content(community, :wiki, wiki_attrs) + + new_wiki_attrs = mock_attrs(:wiki, %{community_id: community.id, readme: "new readme"}) + {:ok, _} = CMS.sync_github_content(community, :wiki, new_wiki_attrs) + {:ok, new_wiki} = CommunityWiki |> ORM.find(wiki.id) + + assert new_wiki.readme == "new readme" + end + + test "can add contributor to wiki", ~m(community wiki_attrs)a do + {:ok, wiki} = CMS.sync_github_content(community, :wiki, wiki_attrs) + cur_contributors = wiki.contributors + + contributor_attrs = mock_attrs(:github_contributor) + {:ok, wiki} = CMS.add_contributor(wiki, contributor_attrs) + update_contributors = wiki.contributors + + assert length(update_contributors) == 1 + length(cur_contributors) + end + + test "add some contributor fails", ~m(community wiki_attrs)a do + {:ok, wiki} = CMS.sync_github_content(community, :wiki, wiki_attrs) + cur_contributors = wiki.contributors + + contributor_attrs = mock_attrs(:github_contributor) + {:ok, wiki} = CMS.add_contributor(wiki, contributor_attrs) + update_contributors = wiki.contributors + + assert length(update_contributors) == 1 + length(cur_contributors) + + # add some again + {:error, error} = CMS.add_contributor(wiki, contributor_attrs) + assert error |> Keyword.get(:code) == ecode(:already_exsit) + end + end +end diff --git a/test/mastani_server/statistics/geo_test.exs b/test/mastani_server/statistics/geo_test.exs new file mode 100644 index 000000000..36db28524 --- /dev/null +++ b/test/mastani_server/statistics/geo_test.exs @@ -0,0 +1,48 @@ +defmodule MastaniServer.Test.Statistics.Geo do + use MastaniServer.TestTools + + alias Helper.ORM + alias MastaniServer.{Statistics} + + setup do + insert_geo_data() + + # {:ok, ~m(user_conn guest_conn community)a} + end + + describe "[statistics geo inc] " do + test "geo data can be inc by city" do + {:ok, _} = Statistics.UserGeoInfo |> ORM.find_by(%{city: "成都"}) + + {:ok, _} = Statistics.inc_count("成都") + + {:ok, updated} = Statistics.UserGeoInfo |> ORM.find_by(%{city: "成都"}) + assert updated.value == 1 + {:ok, _} = Statistics.inc_count("成都") + + {:ok, updated} = Statistics.UserGeoInfo |> ORM.find_by(%{city: "成都"}) + assert updated.value == 2 + end + + test "inc with invalid city fails" do + assert {:error, _} = Statistics.inc_count("not_exsit") + end + end + + describe "[statistics geo get] " do + test "can get geo citis info" do + {:ok, infos} = Statistics.list_cities_info() + assert infos.total_count == 0 + + {:ok, _} = Statistics.inc_count("成都") + {:ok, _} = Statistics.inc_count("成都") + {:ok, _} = Statistics.inc_count("广州") + + {:ok, infos} = Statistics.list_cities_info() + + assert infos.total_count == 2 + assert infos |> Enum.any?(&(&1.city == "成都")) + assert infos |> Enum.any?(&(&1.city == "广州")) + end + end +end diff --git a/test/mastani_server_web/mutation/accounts/account_test.exs b/test/mastani_server_web/mutation/accounts/account_test.exs index e476733d7..370857d30 100644 --- a/test/mastani_server_web/mutation/accounts/account_test.exs +++ b/test/mastani_server_web/mutation/accounts/account_test.exs @@ -15,8 +15,16 @@ defmodule MastaniServer.Test.Mutation.Account.Basic do describe "[account update]" do @update_query """ - mutation($profile: UserProfileInput!) { - updateProfile(profile: $profile) { + mutation( + $profile: UserProfileInput!, + $educationBackgrounds: [EduBackgroundInput], + $workBackgrounds: [WorkBackgroundInput] + ) { + updateProfile( + profile: $profile, + educationBackgrounds: $educationBackgrounds, + workBackgrounds: $workBackgrounds, + ) { id nickname education_backgrounds { @@ -44,27 +52,31 @@ defmodule MastaniServer.Test.Mutation.Account.Basic do assert updated["nickname"] == "new nickname" end - test "user can update it's own education_backgrounds", ~m(user)a do + test "user can update it's own backgrounds", ~m(user)a do ownd_conn = simu_conn(:user, user) variables = %{ profile: %{ - nickname: "new nickname", - education_backgrounds: [ - %{ - school: "school", - major: "bad ass" - }, - %{ - school: "school2", - major: "bad ass2" - } - ] - } + nickname: "new nickname" + }, + educationBackgrounds: [ + %{ + school: "school", + major: "bad ass" + }, + %{ + school: "school2", + major: "bad ass2" + } + ], + workBackgrounds: [ + %{ + company: "cps", + title: "CTO" + } + ] } - # assert ownd_conn |> mutation_get_error?(@update_query, variables) - updated = ownd_conn |> mutation_result(@update_query, variables, "updateProfile") assert updated["nickname"] == "new nickname" @@ -72,6 +84,11 @@ defmodule MastaniServer.Test.Mutation.Account.Basic do assert updated["education_backgrounds"] |> length == 2 assert updated["education_backgrounds"] |> Enum.any?(&(&1["school"] == "school")) assert updated["education_backgrounds"] |> Enum.any?(&(&1["major"] == "bad ass")) + + assert updated["work_backgrounds"] |> is_list + assert updated["work_backgrounds"] |> length == 1 + assert updated["work_backgrounds"] |> Enum.any?(&(&1["company"] == "cps")) + assert updated["work_backgrounds"] |> Enum.any?(&(&1["title"] == "CTO")) end test "user update education_backgrounds with invalid data fails", ~m(user)a do @@ -79,68 +96,38 @@ defmodule MastaniServer.Test.Mutation.Account.Basic do variables = %{ profile: %{ - nickname: "new nickname", - education_backgrounds: [ - %{ - major: "bad ass2" - }, - %{ - school: "school2", - major: "bad ass2" - } - ] - } + nickname: "new nickname" + }, + educationBackgrounds: [ + %{ + major: "bad ass2" + }, + %{ + school: "school2", + major: "bad ass2" + } + ] } assert ownd_conn |> mutation_get_error?(@update_query, variables) end - test "user can update it's own work backgrounds", ~m(user)a do - ownd_conn = simu_conn(:user, user) - - variables = %{ - profile: %{ - nickname: "new nickname", - work_backgrounds: [ - %{ - company: "company", - title: "bad ass" - }, - %{ - company: "company 2", - title: "bad ass2" - } - ] - } - } - - # assert ownd_conn |> mutation_get_error?(@update_query, variables) - - updated = ownd_conn |> mutation_result(@update_query, variables, "updateProfile") - assert updated["nickname"] == "new nickname" - - assert updated["work_backgrounds"] |> is_list - assert updated["work_backgrounds"] |> length == 2 - assert updated["work_backgrounds"] |> Enum.any?(&(&1["company"] == "company")) - assert updated["work_backgrounds"] |> Enum.any?(&(&1["title"] == "bad ass")) - end - test "user update work backgrounds with invalid data fails", ~m(user)a do ownd_conn = simu_conn(:user, user) variables = %{ profile: %{ - nickname: "new nickname", - work_backgrounds: [ - %{ - title: "bad ass2" - }, - %{ - company: "school2", - title: "bad ass2" - } - ] - } + nickname: "new nickname" + }, + workBackgrounds: [ + %{ + title: "bad ass2" + }, + %{ + company: "school2", + title: "bad ass2" + } + ] } assert ownd_conn |> mutation_get_error?(@update_query, variables) diff --git a/test/mastani_server_web/mutation/accounts/customization_test.exs b/test/mastani_server_web/mutation/accounts/customization_test.exs new file mode 100644 index 000000000..a5ca68f83 --- /dev/null +++ b/test/mastani_server_web/mutation/accounts/customization_test.exs @@ -0,0 +1,79 @@ +defmodule MastaniServer.Test.Mutation.Account.Customization do + use MastaniServer.TestTools + + # alias MastaniServer.{Accounts} + # alias Helper.ORM + + setup do + {:ok, user} = db_insert(:user) + + user_conn = simu_conn(:user, user) + guest_conn = simu_conn(:guest) + + {:ok, ~m(user_conn guest_conn user)a} + end + + describe "[account customization mutation]" do + @query """ + mutation($userId: ID, $customization: CustomizationInput!) { + setCustomization( userId: $userId, customization: $customization) { + id + customization { + bannerLayout + contentDivider + markViewed + displayDensity + } + } + } + """ + test "user can set customization", ~m(user_conn user)a do + ownd_conn = simu_conn(:user, user) + + variables = %{ + customization: %{ + bannerLayout: "BRIEF", + contentDivider: true, + markViewed: false, + displayDensity: "25" + } + } + + result = user_conn |> mutation_result(@query, variables, "setCustomization") + + assert result["customization"]["bannerLayout"] == "brief" + assert result["customization"]["contentDivider"] == true + assert result["customization"]["markViewed"] == false + assert result["customization"]["displayDensity"] == "25" + end + + test "user set customization with invalid attr fails", ~m(user_conn user)a do + ownd_conn = simu_conn(:user, user) + + variables1 = %{ + customization: %{ + bannerLayout: "OTHER" + } + } + + variables2 = %{ + customization: %{ + contentsLayout: "OTHER" + } + } + + assert user_conn |> mutation_get_error?(@query, variables1) + assert user_conn |> mutation_get_error?(@query, variables2) + end + + test "unlogin user set customization fails", ~m(guest_conn)a do + variables = %{ + customization: %{ + bannerLayout: "DIGEST" + } + } + + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + end +end diff --git a/test/mastani_server_web/mutation/accounts/fans_test.exs b/test/mastani_server_web/mutation/accounts/fans_test.exs index 3caae4d4f..310754c44 100644 --- a/test/mastani_server_web/mutation/accounts/fans_test.exs +++ b/test/mastani_server_web/mutation/accounts/fans_test.exs @@ -20,6 +20,7 @@ defmodule MastaniServer.Test.Mutation.Accounts.Fans do mutation($userId: ID!) { follow(userId: $userId) { id + viewerHasFollowed } } """ @@ -30,6 +31,7 @@ defmodule MastaniServer.Test.Mutation.Accounts.Fans do followed = user_conn |> mutation_result(@query, variables, "follow") assert followed["id"] == to_string(user2.id) + assert followed["viewerHasFollowed"] == true end test "login user follow other user twice fails", ~m(user_conn)a do @@ -70,16 +72,16 @@ defmodule MastaniServer.Test.Mutation.Accounts.Fans do {:ok, user2} = db_insert(:user) {:ok, _followeer} = user |> Accounts.follow(user2) - {:ok, found} = User |> ORM.find(user.id, preload: :followers) + {:ok, found} = User |> ORM.find(user2.id, preload: :followers) assert found |> Map.get(:followers) |> length == 1 variables = %{userId: user2.id} user_conn |> mutation_result(@query, variables, "undoFollow") - {:ok, found} = User |> ORM.find(user.id, preload: :followers) + {:ok, found} = User |> ORM.find(user2.id, preload: :followers) assert found |> Map.get(:followers) |> length == 0 - {:ok, found} = User |> ORM.find(user.id, preload: :followings) + {:ok, found} = User |> ORM.find(user2.id, preload: :followings) assert found |> Map.get(:followings) |> length == 0 end end diff --git a/test/mastani_server_web/mutation/accounts/favorite_category_test.exs b/test/mastani_server_web/mutation/accounts/favorite_category_test.exs index 1bf060604..de517c26a 100644 --- a/test/mastani_server_web/mutation/accounts/favorite_category_test.exs +++ b/test/mastani_server_web/mutation/accounts/favorite_category_test.exs @@ -9,11 +9,14 @@ defmodule MastaniServer.Test.Mutation.Accounts.FavoriteCategory do setup do {:ok, user} = db_insert(:user) {:ok, post} = db_insert(:post) + {:ok, job} = db_insert(:job) + {:ok, video} = db_insert(:video) + {:ok, repo} = db_insert(:repo) user_conn = simu_conn(:user, user) guest_conn = simu_conn(:guest) - {:ok, ~m(user_conn guest_conn user post)a} + {:ok, ~m(user_conn guest_conn user post job video repo)a} end describe "[Accounts FavoriteCategory CURD]" do @@ -22,6 +25,7 @@ defmodule MastaniServer.Test.Mutation.Accounts.FavoriteCategory do createFavoriteCategory(title: $title, private: $private) { id title + lastUpdated } } """ @@ -32,6 +36,7 @@ defmodule MastaniServer.Test.Mutation.Accounts.FavoriteCategory do {: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 @@ -43,11 +48,17 @@ defmodule MastaniServer.Test.Mutation.Accounts.FavoriteCategory do @query """ mutation($id: ID!, $title: String, $desc: String, $private: Boolean) { - updateFavoriteCategory(id: $id, title: $title, desc: $desc, private: $private) { + updateFavoriteCategory( + id: $id + title: $title + desc: $desc + private: $private + ) { id title desc private + lastUpdated } } """ @@ -67,6 +78,7 @@ defmodule MastaniServer.Test.Mutation.Accounts.FavoriteCategory do assert updated["desc"] == "new desc" assert updated["private"] == true assert updated["title"] == "new title" + assert updated["lastUpdated"] != nil end @query """ @@ -86,15 +98,64 @@ defmodule MastaniServer.Test.Mutation.Accounts.FavoriteCategory do 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){ + setFavorites(id: $id, thread: $thread, categoryId: $categoryId) { id title totalCount + lastUpdated } } """ @@ -102,39 +163,144 @@ defmodule MastaniServer.Test.Mutation.Accounts.FavoriteCategory do test_category = "test category" {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - variables = %{id: post.id, categoryId: category.id} + 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 video to favorites category", ~m(user user_conn video)a do + test_category = "test category" + {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) + + variables = %{id: video.id, thread: "VIDEO", categoryId: category.id} + created = user_conn |> mutation_result(@query, variables, "setFavorites") + {:ok, found} = CMS.VideoFavorite |> ORM.find_by(%{video_id: video.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.video_id == video.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){ + unsetFavorites(id: $id, thread: $thread, categoryId: $categoryId) { id title totalCount + lastUpdated } } """ - test "user can unset favorites category", ~m(user user_conn post)a do + 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, categoryId: category.id} + 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 video to favorites category", ~m(user user_conn video)a do + test_category = "test category" + {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) + {:ok, _favorite_category} = Accounts.set_favorites(user, :video, video.id, category.id) + + {:ok, category} = Accounts.FavoriteCategory |> ORM.find(category.id) + assert category.total_count == 1 + assert category.last_updated != nil + + variables = %{id: video.id, thread: "VIDEO", 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.VideoFavorite |> ORM.find_by(%{video_id: video.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/mastani_server_web/mutation/cms/cheatsheet_test.exs b/test/mastani_server_web/mutation/cms/cheatsheet_test.exs new file mode 100644 index 000000000..aa3e877cf --- /dev/null +++ b/test/mastani_server_web/mutation/cms/cheatsheet_test.exs @@ -0,0 +1,85 @@ +defmodule MastaniServer.Test.Mutation.CMS.Cheatsheet do + use MastaniServer.TestTools + + alias Helper.ORM + alias MastaniServer.CMS + + setup do + {:ok, user} = db_insert(:user) + {:ok, community} = db_insert(:community) + + cheatsheet_attrs = mock_attrs(:cheatsheet, %{community_id: community.id}) + {:ok, cheatsheet} = CMS.sync_github_content(community, :cheatsheet, cheatsheet_attrs) + + user_conn = simu_conn(:user) + guest_conn = simu_conn(:guest) + + {:ok, ~m(user_conn guest_conn community user cheatsheet)a} + end + + @sync_cheatsheet_query """ + mutation($communityId: ID!, $readme: String!, $lastSync: String!){ + syncCheatsheet(communityId: $communityId, readme: $readme, lastSync: $lastSync) { + id + readme + } + } + """ + test "login user can sync cheatsheet", ~m(community)a do + {:ok, user} = db_insert(:user) + user_conn = simu_conn(:user, user) + + cheatsheet_attrs = mock_attrs(:cheatsheet) |> camelize_map_key + + variables = cheatsheet_attrs |> Map.merge(%{communityId: community.id}) + created = user_conn |> mutation_result(@sync_cheatsheet_query, variables, "syncCheatsheet") + + {:ok, cheatsheet} = ORM.find(CMS.CommunityCheatsheet, created["id"]) + + assert created["id"] == to_string(cheatsheet.id) + assert created["readme"] == to_string(cheatsheet.readme) + end + + @add_cheatsheet_contribotor_query """ + mutation($id: ID!, $contributor: GithubContributorInput!){ + addCheatsheetContributor(id: $id, contributor: $contributor) { + id + readme + contributors { + nickname + } + } + } + """ + test "login user can add contributor to an exsit cheatsheet", ~m(cheatsheet)a do + {:ok, user} = db_insert(:user) + user_conn = simu_conn(:user, user) + + contributor_attrs = mock_attrs(:github_contributor) + variables = %{id: cheatsheet.id, contributor: contributor_attrs} + + created = + user_conn + |> mutation_result(@add_cheatsheet_contribotor_query, variables, "addCheatsheetContributor") + + assert created["contributors"] |> length == 4 + end + + test "add some contributor fails", ~m(cheatsheet)a do + {:ok, user} = db_insert(:user) + user_conn = simu_conn(:user, user) + + contributor_attrs = mock_attrs(:github_contributor) + variables = %{id: cheatsheet.id, contributor: contributor_attrs} + + user_conn + |> mutation_result(@add_cheatsheet_contribotor_query, variables, "addCheatshhetContributor") + + assert user_conn + |> mutation_get_error?( + @add_cheatsheet_contribotor_query, + variables, + ecode(:already_exsit) + ) + end +end diff --git a/test/mastani_server_web/mutation/cms/cms_test.exs b/test/mastani_server_web/mutation/cms/cms_test.exs index eb3598087..6f99b2aae 100644 --- a/test/mastani_server_web/mutation/cms/cms_test.exs +++ b/test/mastani_server_web/mutation/cms/cms_test.exs @@ -187,13 +187,15 @@ defmodule MastaniServer.Test.Mutation.CMS.Basic do describe "[mutation cms tag]" do @create_tag_query """ - mutation($thread: CmsThread!, $title: String!, $color: RainbowColorEnum!, $communityId: ID!) { - createTag(thread: $thread, title: $title, color: $color, communityId: $communityId) { + mutation($thread: CmsThread!, $title: String!, $color: RainbowColorEnum!, $communityId: ID!, $topic: String) { + createTag(thread: $thread, title: $title, color: $color, communityId: $communityId, topic: $topic) { id title color thread - + topic { + title + } community { id logo @@ -202,7 +204,8 @@ defmodule MastaniServer.Test.Mutation.CMS.Basic do } } """ - test "create tag with valid attrs, has default POST thread", ~m(community)a do + test "create tag with valid attrs, has default POST thread and default posts topic", + ~m(community)a do variables = mock_attrs(:tag, %{communityId: community.id}) passport_rules = %{community.title => %{"post.tag.create" => true}} @@ -215,11 +218,33 @@ defmodule MastaniServer.Test.Mutation.CMS.Basic do assert created["id"] == to_string(found.id) assert found.thread == "post" + assert created["topic"]["title"] == "posts" assert belong_community["id"] == to_string(community.id) end + test "can create some tag on different topic", ~m(community)a do + variables = mock_attrs(:tag, %{communityId: community.id, topic: "city"}) + + passport_rules = %{community.title => %{"post.tag.create" => true}} + rule_conn = simu_conn(:user, cms: passport_rules) + + created = rule_conn |> mutation_result(@create_tag_query, variables, "createTag") + assert created["title"] == variables.title + assert created["topic"]["title"] == "city" + + assert rule_conn + |> mutation_get_error?(@create_tag_query, variables) + + variables = variables |> Map.merge(%{topic: "news"}) + created = rule_conn |> mutation_result(@create_tag_query, variables, "createTag") + assert created["title"] == variables.title + assert created["topic"]["title"] == "news" + end + test "auth user create duplicate tag fails", ~m(community)a do variables = mock_attrs(:tag, %{communityId: community.id}) + # IO.inspect variables, label: "hello variables" + passport_rules = %{community.title => %{"post.tag.create" => true}} rule_conn = simu_conn(:user, cms: passport_rules) @@ -271,6 +296,7 @@ defmodule MastaniServer.Test.Mutation.CMS.Basic do rule_conn = simu_conn(:user, cms: passport_rules) updated = rule_conn |> mutation_result(@update_tag_query, variables, "updateTag") + assert updated["color"] == "green" assert updated["title"] == "new title" end @@ -659,6 +685,19 @@ defmodule MastaniServer.Test.Mutation.CMS.Basic do assert guest_conn |> mutation_get_error?(@subscribe_query, variables, ecode(:account_login)) end + test "subscribed community should inc it's own geo info", ~m(user community)a do + login_conn = simu_conn(:user, user) + + variables = %{communityId: community.id} + _created = login_conn |> mutation_result(@subscribe_query, variables, "subscribeCommunity") + {:ok, community} = Community |> ORM.find(community.id) + + geo_info_data = community.geo_info |> Map.get("data") + update_geo_city = geo_info_data |> Enum.find(fn g -> g["city"] == "成都" end) + + assert update_geo_city["value"] == 1 + end + @unsubscribe_query """ mutation($communityId: ID!){ unsubscribeCommunity(communityId: $communityId) { @@ -708,6 +747,30 @@ defmodule MastaniServer.Test.Mutation.CMS.Basic do assert guest_conn |> mutation_get_error?(@unsubscribe_query, variables, ecode(:account_login)) end + + test "unsubscribed community should dec it's own geo info", ~m(user community)a do + login_conn = simu_conn(:user, user) + + variables = %{communityId: community.id} + _created = login_conn |> mutation_result(@subscribe_query, variables, "subscribeCommunity") + {:ok, community} = Community |> ORM.find(community.id) + + geo_info_data = community.geo_info |> Map.get("data") + update_geo_city = geo_info_data |> Enum.find(fn g -> g["city"] == "成都" end) + + assert update_geo_city["value"] == 1 + + variables = %{communityId: community.id} + + login_conn |> mutation_result(@unsubscribe_query, variables, "unsubscribeCommunity") + + {:ok, community} = Community |> ORM.find(community.id) + + geo_info_data = community.geo_info |> Map.get("data") + update_geo_city = geo_info_data |> Enum.find(fn g -> g["city"] == "成都" end) + + assert update_geo_city["value"] == 0 + end end describe "[passport]" do diff --git a/test/mastani_server_web/mutation/cms/job_comment_test.exs b/test/mastani_server_web/mutation/cms/job_comment_test.exs index a2d4ac747..05894d92b 100644 --- a/test/mastani_server_web/mutation/cms/job_comment_test.exs +++ b/test/mastani_server_web/mutation/cms/job_comment_test.exs @@ -121,4 +121,89 @@ defmodule MastaniServer.Test.Mutation.JobComment do test "TODO owner can NOT delete comment when comment has created after 3 hours" do end end + + describe "[job 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: "JOB_COMMENT", id: comment.id} + user_conn |> mutation_result(@like_comment_query, variables, "likeComment") + + {:ok, found} = CMS.JobComment |> ORM.find(comment.id, preload: :likes) + + assert found.likes |> Enum.any?(&(&1.job_comment_id == comment.id)) + end + + @undo_like_comment_query """ + mutation($thread: CmsComment!, $id: ID!) { + undoLikeComment(thread: $thread, id: $id) { + id + } + } + """ + test "login user can undo a like action to comment", ~m(user comment)a do + variables = %{thread: "JOB_COMMENT", id: comment.id} + user_conn = simu_conn(:user, user) + user_conn |> mutation_result(@like_comment_query, variables, "likeComment") + + {:ok, found} = CMS.JobComment |> ORM.find(comment.id, preload: :likes) + assert found.likes |> Enum.any?(&(&1.job_comment_id == comment.id)) + + user_conn |> mutation_result(@undo_like_comment_query, variables, "undoLikeComment") + + {:ok, found} = CMS.JobComment |> ORM.find(comment.id, preload: :likes) + assert false == found.likes |> Enum.any?(&(&1.job_comment_id == comment.id)) + end + + @dislike_comment_query """ + mutation($thread: CmsComment!, $id: ID!) { + dislikeComment(thread: $thread, id: $id) { + id + } + } + """ + test "login user can dislike a comment", ~m(user_conn comment)a do + variables = %{thread: "JOB_COMMENT", id: comment.id} + user_conn |> mutation_result(@dislike_comment_query, variables, "dislikeComment") + + {:ok, found} = CMS.JobComment |> ORM.find(comment.id, preload: :dislikes) + + assert found.dislikes |> Enum.any?(&(&1.job_comment_id == comment.id)) + end + + @undo_dislike_comment_query """ + mutation($thread: CmsComment!, $id: ID!) { + undoDislikeComment(thread: $thread, id: $id) { + id + } + } + """ + test "login user can undo dislike a comment", ~m(user comment)a do + variables = %{thread: "JOB_COMMENT", id: comment.id} + user_conn = simu_conn(:user, user) + user_conn |> mutation_result(@dislike_comment_query, variables, "dislikeComment") + {:ok, found} = CMS.JobComment |> ORM.find(comment.id, preload: :dislikes) + assert found.dislikes |> Enum.any?(&(&1.job_comment_id == comment.id)) + + user_conn |> mutation_result(@undo_dislike_comment_query, variables, "undoDislikeComment") + + {:ok, found} = CMS.JobComment |> ORM.find(comment.id, preload: :dislikes) + assert false == found.dislikes |> Enum.any?(&(&1.job_comment_id == comment.id)) + end + + test "unloged user do/undo like/dislike comment fails", ~m(guest_conn comment)a do + variables = %{thread: "JOB_COMMENT", id: comment.id} + + assert guest_conn |> mutation_get_error?(@like_comment_query, variables) + assert guest_conn |> mutation_get_error?(@dislike_comment_query, variables) + + assert guest_conn |> mutation_get_error?(@undo_like_comment_query, variables) + assert guest_conn |> mutation_get_error?(@undo_dislike_comment_query, variables) + end + end end diff --git a/test/mastani_server_web/mutation/cms/job_flag_test.exs b/test/mastani_server_web/mutation/cms/job_flag_test.exs index 2eeb189bc..83729bdc5 100644 --- a/test/mastani_server_web/mutation/cms/job_flag_test.exs +++ b/test/mastani_server_web/mutation/cms/job_flag_test.exs @@ -118,10 +118,11 @@ defmodule MastaniServer.Test.Mutation.JobFlag do passport_rules = %{community.raw => %{"job.undo_pin" => true}} rule_conn = simu_conn(:user, cms: passport_rules) + CMS.pin_content(job, community) updated = rule_conn |> mutation_result(@query, variables, "undoPinJob") assert updated["id"] == to_string(job.id) - assert updated["pin"] == false + # assert updated["pin"] == false end test "unauth user undo pin job fails", ~m(user_conn guest_conn community job)a do diff --git a/test/mastani_server_web/mutation/cms/job_reaction_test.exs b/test/mastani_server_web/mutation/cms/job_reaction_test.exs new file mode 100644 index 000000000..927d08d17 --- /dev/null +++ b/test/mastani_server_web/mutation/cms/job_reaction_test.exs @@ -0,0 +1,61 @@ +defmodule MastaniServer.Test.Mutation.JobReaction do + use MastaniServer.TestTools + + alias MastaniServer.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: String!, $thread: CmsThread!) { + 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: String!, $thread: CmsThread!) { + 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/mastani_server_web/mutation/cms/job_test.exs b/test/mastani_server_web/mutation/cms/job_test.exs index 714f8067f..f44a45b30 100644 --- a/test/mastani_server_web/mutation/cms/job_test.exs +++ b/test/mastani_server_web/mutation/cms/job_test.exs @@ -25,9 +25,14 @@ defmodule MastaniServer.Test.Mutation.Job do $communityId: ID!, $company: String!, $companyLogo: String! - $location: String!, + $salary: String!, + $exp: String!, + $education: String!, + $finance: String!, + $scale: String!, + $field: String!, $tags: [Ids] - ){ + ) { createJob( title: $title, body: $body, @@ -36,12 +41,21 @@ defmodule MastaniServer.Test.Mutation.Job do communityId: $communityId, company: $company, companyLogo: $companyLogo, - location: $location, + salary: $salary, + exp: $exp, + education: $education, + finance: $finance, + scale: $scale, + field: $field, tags: $tags ) { id title body + salary + exp + education + field communities { id title @@ -56,11 +70,15 @@ defmodule MastaniServer.Test.Mutation.Job do {:ok, community} = db_insert(:community) job_attr = mock_attrs(:job) - variables = job_attr |> Map.merge(%{communityId: community.id}) - variables = variables |> Map.merge(%{companyLogo: job_attr.company_logo}) + variables = job_attr |> Map.merge(%{communityId: community.id}) |> camelize_map_key created = user_conn |> mutation_result(@create_job_query, variables, "createJob") + assert created["salary"] == variables["salary"] + assert created["exp"] == variables["exp"] + assert created["field"] == variables["field"] + assert created["education"] == variables["education"] + {:ok, found} = ORM.find(CMS.Job, created["id"]) assert created["id"] == to_string(found.id) @@ -90,11 +108,12 @@ defmodule MastaniServer.Test.Mutation.Job do end @query """ - mutation($id: ID!, $title: String, $body: String){ - updateJob(id: $id, title: $title, body: $body) { + mutation($id: ID!, $title: String, $body: String, $salary: String){ + updateJob(id: $id, title: $title, body: $body, salary: $salary) { id title body + salary } } """ @@ -104,7 +123,8 @@ defmodule MastaniServer.Test.Mutation.Job do variables = %{ id: job.id, title: "updated title #{unique_num}", - body: "updated body #{unique_num}" + body: "updated body #{unique_num}", + salary: "15k-20k" } assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) @@ -116,13 +136,15 @@ defmodule MastaniServer.Test.Mutation.Job do variables = %{ id: job.id, title: "updated title #{unique_num}", - body: "updated body #{unique_num}" + body: "updated body #{unique_num}", + salary: "15k-20k" } updated = owner_conn |> mutation_result(@query, variables, "updateJob") assert updated["title"] == variables.title assert updated["body"] == variables.body + assert updated["salary"] == variables.salary end test "login user with auth passport update a job", ~m(job)a do diff --git a/test/mastani_server_web/mutation/cms/post_flag_test.exs b/test/mastani_server_web/mutation/cms/post_flag_test.exs index 2f33a12e0..f9d1b8f47 100644 --- a/test/mastani_server_web/mutation/cms/post_flag_test.exs +++ b/test/mastani_server_web/mutation/cms/post_flag_test.exs @@ -118,10 +118,10 @@ defmodule MastaniServer.Test.Mutation.PostFlag do passport_rules = %{community.raw => %{"post.undo_pin" => true}} rule_conn = simu_conn(:user, cms: passport_rules) + CMS.pin_content(post, community, "posts") updated = rule_conn |> mutation_result(@query, variables, "undoPinPost") assert updated["id"] == to_string(post.id) - assert updated["pin"] == false end test "unauth user undo pin post fails", ~m(user_conn guest_conn community post)a do diff --git a/test/mastani_server_web/mutation/cms/post_reaction_test.exs b/test/mastani_server_web/mutation/cms/post_reaction_test.exs new file mode 100644 index 000000000..296252d2e --- /dev/null +++ b/test/mastani_server_web/mutation/cms/post_reaction_test.exs @@ -0,0 +1,61 @@ +defmodule MastaniServer.Test.Mutation.PostReaction do + use MastaniServer.TestTools + + alias MastaniServer.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: String!, $thread: CmsThread!) { + 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: String!, $thread: CmsThread!) { + 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/mastani_server_web/mutation/cms/post_test.exs b/test/mastani_server_web/mutation/cms/post_test.exs index 14ed7cae7..ba4c69a16 100644 --- a/test/mastani_server_web/mutation/cms/post_test.exs +++ b/test/mastani_server_web/mutation/cms/post_test.exs @@ -16,8 +16,24 @@ defmodule MastaniServer.Test.Mutation.Post do describe "[mutation post curd]" do @create_post_query """ - mutation($title: String!, $body: String!, $digest: String!, $length: Int!, $communityId: ID!, $tags: [Ids]){ - createPost(title: $title, body: $body, digest: $digest, length: $length, communityId: $communityId, tags: $tags) { + mutation( + $title: String! + $body: String! + $digest: String! + $length: Int! + $communityId: ID! + $tags: [Ids] + $topic: CmsTopic + ) { + createPost( + title: $title + body: $body + digest: $digest + length: $length + communityId: $communityId + tags: $tags + topic: $topic + ) { title body id @@ -39,6 +55,19 @@ defmodule MastaniServer.Test.Mutation.Post do assert {:ok, _} = ORM.find_by(CMS.Author, user_id: user.id) end + # NOTE: this test is IMPORTANT, cause json_codec: Jason in router will cause + # server crash when GraphQL parse error + test "create post with missing non_null field should get 200 error" do + {:ok, user} = db_insert(:user) + user_conn = simu_conn(:user, user) + + {:ok, community} = db_insert(:community) + post_attr = mock_attrs(:post) + variables = post_attr |> Map.merge(%{communityId: community.id}) |> Map.delete(:title) + + assert user_conn |> mutation_get_error?(@create_post_query, variables) + end + test "can create post with tags" do {:ok, user} = db_insert(:user) user_conn = simu_conn(:user, user) @@ -111,11 +140,12 @@ defmodule MastaniServer.Test.Mutation.Post do end @query """ - mutation($id: ID!, $title: String, $body: String){ - updatePost(id: $id, title: $title, body: $body) { + mutation($id: ID!, $title: String, $body: String, $copyRight: String){ + updatePost(id: $id, title: $title, body: $body, copyRight: $copyRight) { id title body + copyRight } } """ @@ -137,13 +167,15 @@ defmodule MastaniServer.Test.Mutation.Post do variables = %{ id: post.id, title: "updated title #{unique_num}", - body: "updated body #{unique_num}" + body: "updated body #{unique_num}", + copyRight: "translate" } updated_post = owner_conn |> mutation_result(@query, variables, "updatePost") assert updated_post["title"] == variables.title assert updated_post["body"] == variables.body + assert updated_post["copyRight"] == variables.copyRight end test "login user with auth passport update a post", ~m(post)a do diff --git a/test/mastani_server_web/mutation/cms/repo_comment_test.exs b/test/mastani_server_web/mutation/cms/repo_comment_test.exs new file mode 100644 index 000000000..d0e9439d3 --- /dev/null +++ b/test/mastani_server_web/mutation/cms/repo_comment_test.exs @@ -0,0 +1,200 @@ +defmodule MastaniServer.Test.Mutation.RepoComment do + use MastaniServer.TestTools + + alias Helper.ORM + alias MastaniServer.CMS + + setup do + {:ok, repo} = db_insert(:repo) + {:ok, user} = db_insert(:user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user) + + {:ok, comment} = CMS.create_comment(:repo, repo.id, "test comment", user) + + {:ok, ~m(user_conn guest_conn repo user comment)a} + end + + describe "[repo comment CURD]" do + @create_comment_query """ + mutation($thread: CmsThread, $id: ID!, $body: String!) { + createComment(thread: $thread, id: $id, body: $body) { + id + body + } + } + """ + test "login user can create comment to a repo", ~m(user_conn repo)a do + variables = %{thread: "REPO", id: repo.id, body: "this a comment"} + created = user_conn |> mutation_result(@create_comment_query, variables, "createComment") + + {:ok, found} = ORM.find(CMS.RepoComment, created["id"]) + + assert created["id"] == to_string(found.id) + end + + test "guest user create comment fails", ~m(guest_conn repo)a do + variables = %{thread: "REPO", id: repo.id, body: "this a comment"} + + assert guest_conn + |> mutation_get_error?(@create_comment_query, variables, ecode(:account_login)) + end + + @delete_comment_query """ + mutation($thread: CmsThread, $id: ID!) { + deleteComment(thread: $thread, id: $id) { + id + body + } + } + """ + test "comment owner can delete comment", ~m(user repo)a do + variables = %{thread: "REPO", id: repo.id, body: "this a comment"} + + user_conn = simu_conn(:user, user) + created = user_conn |> mutation_result(@create_comment_query, variables, "createComment") + + variables = %{thread: "REPO", id: created["id"]} + deleted = user_conn |> mutation_result(@delete_comment_query, variables, "deleteComment") + + assert deleted["id"] == created["id"] + end + + test "unauth user delete comment fails", ~m(user_conn guest_conn repo)a do + variables = %{thread: "REPO", id: repo.id, body: "this a comment"} + {:ok, owner} = db_insert(:user) + owner_conn = simu_conn(:user, owner) + created = owner_conn |> mutation_result(@create_comment_query, variables, "createComment") + + variables = %{thread: "REPO", id: created["id"]} + rule_conn = simu_conn(:user, cms: %{"what.ever" => true}) + + assert user_conn |> mutation_get_error?(@delete_comment_query, variables, ecode(:passport)) + + assert guest_conn + |> mutation_get_error?(@delete_comment_query, variables, ecode(:account_login)) + + assert rule_conn |> mutation_get_error?(@delete_comment_query, variables, ecode(:passport)) + end + + @reply_comment_query """ + mutation($thread: CmsThread!, $id: ID!, $body: String!) { + replyComment(thread: $thread, id: $id, body: $body) { + id + body + replyTo { + id + body + } + } + } + """ + test "login user can reply to a exsit comment", ~m(user_conn comment)a do + variables = %{thread: "REPO", id: comment.id, body: "this a reply"} + replied = user_conn |> mutation_result(@reply_comment_query, variables, "replyComment") + + assert replied["replyTo"] |> Map.get("id") == to_string(comment.id) + end + + test "guest user reply comment fails", ~m(guest_conn comment)a do + variables = %{thread: "REPO", id: comment.id, body: "this a reply"} + + assert guest_conn |> mutation_get_error?(@reply_comment_query, variables) + end + + test "TODO owner can NOT delete comment when comment has replies" do + end + + test "TODO owner can NOT edit comment when comment has replies" do + end + + test "TODO owner can NOT delete comment when comment has created after 3 hours" do + end + end + + describe "[repo 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: "REPO_COMMENT", id: comment.id} + user_conn |> mutation_result(@like_comment_query, variables, "likeComment") + + {:ok, found} = CMS.RepoComment |> ORM.find(comment.id, preload: :likes) + + assert found.likes |> Enum.any?(&(&1.repo_comment_id == comment.id)) + end + + @undo_like_comment_query """ + mutation($thread: CmsComment!, $id: ID!) { + undoLikeComment(thread: $thread, id: $id) { + id + } + } + """ + test "login user can undo a like action to comment", ~m(user comment)a do + variables = %{thread: "REPO_COMMENT", id: comment.id} + user_conn = simu_conn(:user, user) + user_conn |> mutation_result(@like_comment_query, variables, "likeComment") + + {:ok, found} = CMS.RepoComment |> ORM.find(comment.id, preload: :likes) + assert found.likes |> Enum.any?(&(&1.repo_comment_id == comment.id)) + + user_conn |> mutation_result(@undo_like_comment_query, variables, "undoLikeComment") + + {:ok, found} = CMS.RepoComment |> ORM.find(comment.id, preload: :likes) + assert false == found.likes |> Enum.any?(&(&1.repo_comment_id == comment.id)) + end + + @dislike_comment_query """ + mutation($thread: CmsComment!, $id: ID!) { + dislikeComment(thread: $thread, id: $id) { + id + } + } + """ + test "login user can dislike a comment", ~m(user_conn comment)a do + variables = %{thread: "REPO_COMMENT", id: comment.id} + user_conn |> mutation_result(@dislike_comment_query, variables, "dislikeComment") + + {:ok, found} = CMS.RepoComment |> ORM.find(comment.id, preload: :dislikes) + + assert found.dislikes |> Enum.any?(&(&1.repo_comment_id == comment.id)) + end + + @undo_dislike_comment_query """ + mutation($thread: CmsComment!, $id: ID!) { + undoDislikeComment(thread: $thread, id: $id) { + id + } + } + """ + test "login user can undo dislike a comment", ~m(user comment)a do + variables = %{thread: "REPO_COMMENT", id: comment.id} + user_conn = simu_conn(:user, user) + user_conn |> mutation_result(@dislike_comment_query, variables, "dislikeComment") + {:ok, found} = CMS.RepoComment |> ORM.find(comment.id, preload: :dislikes) + assert found.dislikes |> Enum.any?(&(&1.repo_comment_id == comment.id)) + + user_conn |> mutation_result(@undo_dislike_comment_query, variables, "undoDislikeComment") + + {:ok, found} = CMS.RepoComment |> ORM.find(comment.id, preload: :dislikes) + assert false == found.dislikes |> Enum.any?(&(&1.repo_comment_id == comment.id)) + end + + test "unloged user do/undo like/dislike comment fails", ~m(guest_conn comment)a do + variables = %{thread: "REPO_COMMENT", id: comment.id} + + assert guest_conn |> mutation_get_error?(@like_comment_query, variables) + assert guest_conn |> mutation_get_error?(@dislike_comment_query, variables) + + assert guest_conn |> mutation_get_error?(@undo_like_comment_query, variables) + assert guest_conn |> mutation_get_error?(@undo_dislike_comment_query, variables) + end + end +end diff --git a/test/mastani_server_web/mutation/cms/repo_flag_test.exs b/test/mastani_server_web/mutation/cms/repo_flag_test.exs index 9ae71804d..d2f813bc3 100644 --- a/test/mastani_server_web/mutation/cms/repo_flag_test.exs +++ b/test/mastani_server_web/mutation/cms/repo_flag_test.exs @@ -118,10 +118,10 @@ defmodule MastaniServer.Test.Mutation.RepoFlag do passport_rules = %{community.raw => %{"repo.undo_pin" => true}} rule_conn = simu_conn(:user, cms: passport_rules) + CMS.pin_content(repo, community) updated = rule_conn |> mutation_result(@query, variables, "undoPinRepo") assert updated["id"] == to_string(repo.id) - assert updated["pin"] == false end test "unauth user undo pin repo fails", ~m(user_conn guest_conn community repo)a do diff --git a/test/mastani_server_web/mutation/cms/repo_test.exs b/test/mastani_server_web/mutation/cms/repo_test.exs new file mode 100644 index 000000000..04cefdd49 --- /dev/null +++ b/test/mastani_server_web/mutation/cms/repo_test.exs @@ -0,0 +1,80 @@ +defmodule MastaniServer.Test.Mutation.Repo do + use MastaniServer.TestTools + + alias Helper.ORM + alias MastaniServer.CMS + + setup do + {:ok, repo} = db_insert(:repo) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user) + owner_conn = simu_conn(:owner, repo) + + {:ok, ~m(user_conn guest_conn owner_conn repo)a} + end + + describe "[mutation repo curd]" do + @create_repo_query """ + mutation( + $title: String!, + $ownerName: String!, + $ownerUrl: String!, + $repoUrl: String!, + $desc: String!, + $homepageUrl: String, + $readme: String!, + $starCount: Int!, + $issuesCount: Int!, + $prsCount: Int!, + $forkCount: Int!, + $watchCount: Int!, + $license: String, + $releaseTag: String, + $primaryLanguage: RepoLangInput, + $contributors: [RepoContributorInput], + $communityId: ID!, + $tags: [Ids] + ) { + createRepo( + title: $title, + ownerName: $ownerName, + ownerUrl: $ownerUrl, + repoUrl: $repoUrl, + desc: $desc, + homepageUrl: $homepageUrl, + readme: $readme, + starCount: $starCount, + issuesCount: $issuesCount, + prsCount: $prsCount, + forkCount: $forkCount, + watchCount: $watchCount, + primaryLanguage: $primaryLanguage, + license: $license, + releaseTag: $releaseTag, + contributors: $contributors, + communityId: $communityId, + tags: $tags + ) { + id + title + desc + } + } + """ + test "create repo with valid attrs and make sure author exsit" do + {:ok, user} = db_insert(:user) + user_conn = simu_conn(:user, user) + + {:ok, community} = db_insert(:community) + repo_attr = mock_attrs(:repo) |> camelize_map_key + + variables = repo_attr |> Map.merge(%{communityId: community.id}) + created = user_conn |> mutation_result(@create_repo_query, variables, "createRepo") + {:ok, repo} = ORM.find(CMS.Repo, created["id"]) + + assert created["id"] == to_string(repo.id) + assert {:ok, _} = ORM.find_by(CMS.Author, user_id: user.id) + end + end +end diff --git a/test/mastani_server_web/mutation/cms/video_comment_test.exs b/test/mastani_server_web/mutation/cms/video_comment_test.exs new file mode 100644 index 000000000..88eda6787 --- /dev/null +++ b/test/mastani_server_web/mutation/cms/video_comment_test.exs @@ -0,0 +1,200 @@ +defmodule MastaniServer.Test.Mutation.VideoComment do + use MastaniServer.TestTools + + alias Helper.ORM + alias MastaniServer.CMS + + setup do + {:ok, video} = db_insert(:video) + {:ok, user} = db_insert(:user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user) + + {:ok, comment} = CMS.create_comment(:video, video.id, "test comment", user) + + {:ok, ~m(user_conn guest_conn video user comment)a} + end + + describe "[video comment CURD]" do + @create_comment_query """ + mutation($thread: CmsThread, $id: ID!, $body: String!) { + createComment(thread: $thread, id: $id, body: $body) { + id + body + } + } + """ + test "login user can create comment to a video", ~m(user_conn video)a do + variables = %{thread: "VIDEO", id: video.id, body: "this a comment"} + created = user_conn |> mutation_result(@create_comment_query, variables, "createComment") + + {:ok, found} = ORM.find(CMS.VideoComment, created["id"]) + + assert created["id"] == to_string(found.id) + end + + test "guest user create comment fails", ~m(guest_conn video)a do + variables = %{thread: "VIDEO", id: video.id, body: "this a comment"} + + assert guest_conn + |> mutation_get_error?(@create_comment_query, variables, ecode(:account_login)) + end + + @delete_comment_query """ + mutation($thread: CmsThread, $id: ID!) { + deleteComment(thread: $thread, id: $id) { + id + body + } + } + """ + test "comment owner can delete comment", ~m(user video)a do + variables = %{thread: "VIDEO", id: video.id, body: "this a comment"} + + user_conn = simu_conn(:user, user) + created = user_conn |> mutation_result(@create_comment_query, variables, "createComment") + + variables = %{thread: "VIDEO", id: created["id"]} + deleted = user_conn |> mutation_result(@delete_comment_query, variables, "deleteComment") + + assert deleted["id"] == created["id"] + end + + test "unauth user delete comment fails", ~m(user_conn guest_conn video)a do + variables = %{thread: "VIDEO", id: video.id, body: "this a comment"} + {:ok, owner} = db_insert(:user) + owner_conn = simu_conn(:user, owner) + created = owner_conn |> mutation_result(@create_comment_query, variables, "createComment") + + variables = %{thread: "VIDEO", id: created["id"]} + rule_conn = simu_conn(:user, cms: %{"what.ever" => true}) + + assert user_conn |> mutation_get_error?(@delete_comment_query, variables, ecode(:passport)) + + assert guest_conn + |> mutation_get_error?(@delete_comment_query, variables, ecode(:account_login)) + + assert rule_conn |> mutation_get_error?(@delete_comment_query, variables, ecode(:passport)) + end + + @reply_comment_query """ + mutation($thread: CmsThread!, $id: ID!, $body: String!) { + replyComment(thread: $thread, id: $id, body: $body) { + id + body + replyTo { + id + body + } + } + } + """ + test "login user can reply to a exsit comment", ~m(user_conn comment)a do + variables = %{thread: "VIDEO", id: comment.id, body: "this a reply"} + replied = user_conn |> mutation_result(@reply_comment_query, variables, "replyComment") + + assert replied["replyTo"] |> Map.get("id") == to_string(comment.id) + end + + test "guest user reply comment fails", ~m(guest_conn comment)a do + variables = %{thread: "VIDEO", id: comment.id, body: "this a reply"} + + assert guest_conn |> mutation_get_error?(@reply_comment_query, variables) + end + + test "TODO owner can NOT delete comment when comment has replies" do + end + + test "TODO owner can NOT edit comment when comment has replies" do + end + + test "TODO owner can NOT delete comment when comment has created after 3 hours" do + end + end + + describe "[video 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: "VIDEO_COMMENT", id: comment.id} + user_conn |> mutation_result(@like_comment_query, variables, "likeComment") + + {:ok, found} = CMS.VideoComment |> ORM.find(comment.id, preload: :likes) + + assert found.likes |> Enum.any?(&(&1.video_comment_id == comment.id)) + end + + @undo_like_comment_query """ + mutation($thread: CmsComment!, $id: ID!) { + undoLikeComment(thread: $thread, id: $id) { + id + } + } + """ + test "login user can undo a like action to comment", ~m(user comment)a do + variables = %{thread: "VIDEO_COMMENT", id: comment.id} + user_conn = simu_conn(:user, user) + user_conn |> mutation_result(@like_comment_query, variables, "likeComment") + + {:ok, found} = CMS.VideoComment |> ORM.find(comment.id, preload: :likes) + assert found.likes |> Enum.any?(&(&1.video_comment_id == comment.id)) + + user_conn |> mutation_result(@undo_like_comment_query, variables, "undoLikeComment") + + {:ok, found} = CMS.VideoComment |> ORM.find(comment.id, preload: :likes) + assert false == found.likes |> Enum.any?(&(&1.video_comment_id == comment.id)) + end + + @dislike_comment_query """ + mutation($thread: CmsComment!, $id: ID!) { + dislikeComment(thread: $thread, id: $id) { + id + } + } + """ + test "login user can dislike a comment", ~m(user_conn comment)a do + variables = %{thread: "VIDEO_COMMENT", id: comment.id} + user_conn |> mutation_result(@dislike_comment_query, variables, "dislikeComment") + + {:ok, found} = CMS.VideoComment |> ORM.find(comment.id, preload: :dislikes) + + assert found.dislikes |> Enum.any?(&(&1.video_comment_id == comment.id)) + end + + @undo_dislike_comment_query """ + mutation($thread: CmsComment!, $id: ID!) { + undoDislikeComment(thread: $thread, id: $id) { + id + } + } + """ + test "login user can undo dislike a comment", ~m(user comment)a do + variables = %{thread: "VIDEO_COMMENT", id: comment.id} + user_conn = simu_conn(:user, user) + user_conn |> mutation_result(@dislike_comment_query, variables, "dislikeComment") + {:ok, found} = CMS.VideoComment |> ORM.find(comment.id, preload: :dislikes) + assert found.dislikes |> Enum.any?(&(&1.video_comment_id == comment.id)) + + user_conn |> mutation_result(@undo_dislike_comment_query, variables, "undoDislikeComment") + + {:ok, found} = CMS.VideoComment |> ORM.find(comment.id, preload: :dislikes) + assert false == found.dislikes |> Enum.any?(&(&1.video_comment_id == comment.id)) + end + + test "unloged user do/undo like/dislike comment fails", ~m(guest_conn comment)a do + variables = %{thread: "VIDEO_COMMENT", id: comment.id} + + assert guest_conn |> mutation_get_error?(@like_comment_query, variables) + assert guest_conn |> mutation_get_error?(@dislike_comment_query, variables) + + assert guest_conn |> mutation_get_error?(@undo_like_comment_query, variables) + assert guest_conn |> mutation_get_error?(@undo_dislike_comment_query, variables) + end + end +end diff --git a/test/mastani_server_web/mutation/cms/video_flag_test.exs b/test/mastani_server_web/mutation/cms/video_flag_test.exs index 678cd967f..764f4fedd 100644 --- a/test/mastani_server_web/mutation/cms/video_flag_test.exs +++ b/test/mastani_server_web/mutation/cms/video_flag_test.exs @@ -108,7 +108,6 @@ defmodule MastaniServer.Test.Mutation.VideoFlag do mutation($id: ID!, $communityId: ID!){ undoPinVideo(id: $id, communityId: $communityId) { id - pin } } """ @@ -118,10 +117,10 @@ defmodule MastaniServer.Test.Mutation.VideoFlag do passport_rules = %{community.raw => %{"video.undo_pin" => true}} rule_conn = simu_conn(:user, cms: passport_rules) + CMS.pin_content(video, community) updated = rule_conn |> mutation_result(@query, variables, "undoPinVideo") assert updated["id"] == to_string(video.id) - assert updated["pin"] == false end test "unauth user undo pin video fails", ~m(user_conn guest_conn community video)a do diff --git a/test/mastani_server_web/mutation/cms/video_reaction_test.exs b/test/mastani_server_web/mutation/cms/video_reaction_test.exs new file mode 100644 index 000000000..80787461b --- /dev/null +++ b/test/mastani_server_web/mutation/cms/video_reaction_test.exs @@ -0,0 +1,61 @@ +defmodule MastaniServer.Test.Mutation.VideoReaction do + use MastaniServer.TestTools + + alias MastaniServer.CMS + + setup do + {:ok, video} = db_insert(:video) + {:ok, user} = db_insert(:user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user, user) + + {:ok, ~m(user_conn guest_conn video user)a} + end + + describe "[video star]" do + @query """ + mutation($id: ID!, $action: String!, $thread: CmsThread!) { + reaction(id: $id, action: $action, thread: $thread) { + id + } + } + """ + test "login user can star a video", ~m(user_conn video)a do + variables = %{id: video.id, thread: "VIDEO", action: "STAR"} + created = user_conn |> mutation_result(@query, variables, "reaction") + + assert created["id"] == to_string(video.id) + end + + test "unauth user star a video fails", ~m(guest_conn video)a do + variables = %{id: video.id, thread: "VIDEO", action: "STAR"} + + assert guest_conn + |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + + @query """ + mutation($id: ID!, $action: String!, $thread: CmsThread!) { + undoReaction(id: $id, action: $action, thread: $thread) { + id + } + } + """ + test "login user can undo star a video", ~m(user_conn video user)a do + {:ok, _} = CMS.reaction(:video, :star, video.id, user) + + variables = %{id: video.id, thread: "VIDEO", action: "STAR"} + updated = user_conn |> mutation_result(@query, variables, "undoReaction") + + assert updated["id"] == to_string(video.id) + end + + test "unauth user undo star a video fails", ~m(guest_conn video)a do + variables = %{id: video.id, thread: "VIDEO", action: "STAR"} + + assert guest_conn + |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + end +end diff --git a/test/mastani_server_web/mutation/cms/video_test.exs b/test/mastani_server_web/mutation/cms/video_test.exs index e8c1ce8ca..2554470ed 100644 --- a/test/mastani_server_web/mutation/cms/video_test.exs +++ b/test/mastani_server_web/mutation/cms/video_test.exs @@ -57,7 +57,7 @@ defmodule MastaniServer.Test.Mutation.Video do user_conn = simu_conn(:user, user) {:ok, community} = db_insert(:community) - video_attr = mock_attrs(:video) + video_attr = mock_attrs(:video) |> camelize_map_key variables = video_attr |> Map.merge(%{communityId: community.id}) created = user_conn |> mutation_result(@create_video_query, variables, "createVideo") diff --git a/test/mastani_server_web/mutation/cms/wiki_test.exs b/test/mastani_server_web/mutation/cms/wiki_test.exs new file mode 100644 index 000000000..8440499d6 --- /dev/null +++ b/test/mastani_server_web/mutation/cms/wiki_test.exs @@ -0,0 +1,80 @@ +defmodule MastaniServer.Test.Mutation.CMS.Wiki do + use MastaniServer.TestTools + + alias Helper.ORM + alias MastaniServer.CMS + + setup do + {:ok, user} = db_insert(:user) + {:ok, community} = db_insert(:community) + + wiki_attrs = mock_attrs(:wiki, %{community_id: community.id}) + {:ok, wiki} = CMS.sync_github_content(community, :wiki, wiki_attrs) + + user_conn = simu_conn(:user) + guest_conn = simu_conn(:guest) + + {:ok, ~m(user_conn guest_conn community user wiki)a} + end + + @sync_wiki_query """ + mutation($communityId: ID!, $readme: String!, $lastSync: String!){ + syncWiki(communityId: $communityId, readme: $readme, lastSync: $lastSync) { + id + readme + } + } + """ + test "login user can sync wiki", ~m(community)a do + {:ok, user} = db_insert(:user) + user_conn = simu_conn(:user, user) + + # IO.inspect(mock_attrs(:wiki) |> camelize_map_key, label: "hello ...") + wiki_attrs = mock_attrs(:wiki) |> camelize_map_key + + variables = wiki_attrs |> Map.merge(%{communityId: community.id}) + created = user_conn |> mutation_result(@sync_wiki_query, variables, "syncWiki") + + {:ok, wiki} = ORM.find(CMS.CommunityWiki, created["id"]) + + assert created["id"] == to_string(wiki.id) + assert created["readme"] == to_string(wiki.readme) + end + + @add_wiki_contribotor_query """ + mutation($id: ID!, $contributor: GithubContributorInput!){ + addWikiContributor(id: $id, contributor: $contributor) { + id + readme + contributors { + nickname + } + } + } + """ + test "login user can add contributor to an exsit wiki", ~m(wiki)a do + {:ok, user} = db_insert(:user) + user_conn = simu_conn(:user, user) + + contributor_attrs = mock_attrs(:github_contributor) + variables = %{id: wiki.id, contributor: contributor_attrs} + + created = + user_conn |> mutation_result(@add_wiki_contribotor_query, variables, "addWikiContributor") + + assert created["contributors"] |> length == 4 + end + + test "add some contributor fails", ~m(wiki)a do + {:ok, user} = db_insert(:user) + user_conn = simu_conn(:user, user) + + contributor_attrs = mock_attrs(:github_contributor) + variables = %{id: wiki.id, contributor: contributor_attrs} + + user_conn |> mutation_result(@add_wiki_contribotor_query, variables, "addWikiContributor") + + assert user_conn + |> mutation_get_error?(@add_wiki_contribotor_query, variables, ecode(:already_exsit)) + end +end diff --git a/test/mastani_server_web/mutation/delivery/delivery_test.exs b/test/mastani_server_web/mutation/delivery/delivery_test.exs index 6662d5609..c99e8d708 100644 --- a/test/mastani_server_web/mutation/delivery/delivery_test.exs +++ b/test/mastani_server_web/mutation/delivery/delivery_test.exs @@ -14,7 +14,7 @@ defmodule MastaniServer.Test.Mutation.Delivery do @account_query """ query($filter: MessagesFilter!) { - account { + user { id mentions(filter: $filter) { entries { @@ -73,7 +73,7 @@ defmodule MastaniServer.Test.Mutation.Delivery do rule_conn |> mutation_result(@query, variables, "publishSystemNotification") variables = %{filter: %{page: 1, size: 20, read: false}} - result = user_conn |> query_result(@account_query, variables, "account") + result = user_conn |> query_result(@account_query, variables, "user") sys_notifications = result["sysNotifications"] assert sys_notifications["totalCount"] == 1 @@ -107,7 +107,7 @@ defmodule MastaniServer.Test.Mutation.Delivery do mock_sys_notification(3) variables = %{filter: %{page: 1, size: 20, read: false}} - result = user_conn |> query_result(@account_query, variables, "account") + result = user_conn |> query_result(@account_query, variables, "user") notifications = result["sysNotifications"] assert notifications["totalCount"] == 3 @@ -118,14 +118,14 @@ defmodule MastaniServer.Test.Mutation.Delivery do user_conn |> mutation_result(@query, variables, "markSysNotificationRead") variables = %{filter: %{page: 1, size: 20, read: false}} - result = user_conn |> query_result(@account_query, variables, "account") + result = user_conn |> query_result(@account_query, variables, "user") notifications = result["sysNotifications"] assert notifications["totalCount"] == 2 variables = %{filter: %{page: 1, size: 20, read: true}} - result = user_conn |> query_result(@account_query, variables, "account") + result = user_conn |> query_result(@account_query, variables, "user") notifications = result["sysNotifications"] assert notifications["totalCount"] == 1 end @@ -190,7 +190,7 @@ defmodule MastaniServer.Test.Mutation.Delivery do mock_mentions_for(user, 3) variables = %{filter: %{page: 1, size: 20, read: false}} - result = user_conn |> query_result(@account_query, variables, "account") + result = user_conn |> query_result(@account_query, variables, "user") mentions = result["mentions"] assert mentions["totalCount"] == 3 @@ -201,13 +201,13 @@ defmodule MastaniServer.Test.Mutation.Delivery do variables = %{filter: %{page: 1, size: 20, read: false}} - result = user_conn |> query_result(@account_query, variables, "account") + result = user_conn |> query_result(@account_query, variables, "user") mentions = result["mentions"] assert mentions["totalCount"] == 2 variables = %{filter: %{page: 1, size: 20, read: true}} - result = user_conn |> query_result(@account_query, variables, "account") + result = user_conn |> query_result(@account_query, variables, "user") mentions = result["mentions"] assert mentions["totalCount"] == 1 end @@ -226,7 +226,7 @@ defmodule MastaniServer.Test.Mutation.Delivery do mock_mentions_for(user, 3) variables = %{filter: %{page: 1, size: 20, read: false}} - result = user_conn |> query_result(@account_query, variables, "account") + result = user_conn |> query_result(@account_query, variables, "user") mentions = result["mentions"] # IO.inspect mentions, label: "mentions" assert mentions["totalCount"] == 3 @@ -234,7 +234,7 @@ defmodule MastaniServer.Test.Mutation.Delivery do user_conn |> mutation_result(@query, %{}, "markMentionReadAll") variables = %{filter: %{page: 1, size: 20, read: false}} - result = user_conn |> query_result(@account_query, variables, "account") + result = user_conn |> query_result(@account_query, variables, "user") mentions = result["mentions"] assert mentions["totalCount"] == 0 @@ -254,7 +254,7 @@ defmodule MastaniServer.Test.Mutation.Delivery do mock_notifications_for(user, 3) variables = %{filter: %{page: 1, size: 20, read: false}} - result = user_conn |> query_result(@account_query, variables, "account") + result = user_conn |> query_result(@account_query, variables, "user") notifications = result["notifications"] assert notifications["totalCount"] == 3 @@ -265,14 +265,14 @@ defmodule MastaniServer.Test.Mutation.Delivery do user_conn |> mutation_result(@query, variables, "markNotificationRead") variables = %{filter: %{page: 1, size: 20, read: false}} - result = user_conn |> query_result(@account_query, variables, "account") + result = user_conn |> query_result(@account_query, variables, "user") notifications = result["notifications"] assert notifications["totalCount"] == 2 variables = %{filter: %{page: 1, size: 20, read: true}} - result = user_conn |> query_result(@account_query, variables, "account") + result = user_conn |> query_result(@account_query, variables, "user") notifications = result["notifications"] assert notifications["totalCount"] == 1 end @@ -291,14 +291,14 @@ defmodule MastaniServer.Test.Mutation.Delivery do mock_notifications_for(user, 3) variables = %{filter: %{page: 1, size: 20, read: false}} - result = user_conn |> query_result(@account_query, variables, "account") + result = user_conn |> query_result(@account_query, variables, "user") notifications = result["notifications"] assert notifications["totalCount"] == 3 user_conn |> mutation_result(@query, %{}, "markNotificationReadAll") variables = %{filter: %{page: 1, size: 20, read: false}} - result = user_conn |> query_result(@account_query, variables, "account") + result = user_conn |> query_result(@account_query, variables, "user") notifications = result["notifications"] assert notifications["totalCount"] == 0 diff --git a/test/mastani_server_web/query/accounts/account_test.exs b/test/mastani_server_web/query/accounts/account_test.exs index c01dd5f19..3e562c65f 100644 --- a/test/mastani_server_web/query/accounts/account_test.exs +++ b/test/mastani_server_web/query/accounts/account_test.exs @@ -2,58 +2,27 @@ defmodule MastaniServer.Test.Query.Account.Basic do use MastaniServer.TestTools import Helper.Utils, only: [get_config: 2] - alias MastaniServer.CMS + + alias Helper.ORM + alias MastaniServer.{Accounts, CMS} @default_subscribed_communities get_config(:general, :default_subscribed_communities) setup do {:ok, user} = db_insert(:user) guest_conn = simu_conn(:guest) - # user_conn = simu_conn(:user, user) - {:ok, ~m(guest_conn user)a} - end - - describe "[account session state]" do - @query """ - query { - sessionState { - isValid - user { - id - } - } - } - """ - test "guest user should get false sessionState", ~m(guest_conn)a do - results = guest_conn |> query_result(@query, %{}, "sessionState") - assert results["isValid"] == false - assert results["user"] == nil - end - - test "login user should get true sessionState", ~m(user)a do - user_conn = simu_conn(:user, user) - results = user_conn |> query_result(@query, %{}, "sessionState") - - assert results["isValid"] == true - assert results["user"] |> Map.get("id") == to_string(user.id) - end - - test "user with invalid token get false sessionState" do - user_conn = simu_conn(:invalid_token) - results = user_conn |> query_result(@query, %{}, "sessionState") - - assert results["isValid"] == false - assert results["user"] == nil - end + user_conn = simu_conn(:user, user) + {:ok, ~m(guest_conn user_conn user)a} end describe "[account basic]" do @query """ - query($id: ID!) { + query($id: ID) { user(id: $id) { id nickname bio + views cmsPassport cmsPassportString educationBackgrounds { @@ -75,6 +44,30 @@ defmodule MastaniServer.Test.Query.Account.Basic do assert results["nickname"] == user.nickname assert results["educationBackgrounds"] == [] assert results["workBackgrounds"] == [] + assert results["cmsPassport"] == nil + end + + test "login user can get it's own profile", ~m(user_conn user)a do + results = user_conn |> query_result(@query, %{}, "user") + assert results["id"] == to_string(user.id) + end + + test "user's views +1 after visit", ~m(guest_conn user)a do + {:ok, target_user} = ORM.find(Accounts.User, user.id) + assert target_user.views == 0 + + variables = %{id: user.id} + results = guest_conn |> query_result(@query, variables, "user") + assert results["views"] == 1 + end + + test "login newbie user can get own empty cms_passport", ~m(user)a do + user_conn = simu_conn(:user, user) + variables = %{id: user.id} + results = user_conn |> query_result(@query, variables, "user") + + assert results["cmsPassport"] == %{} + assert results["cmsPassportString"] == "{}" end @valid_rules %{ @@ -96,14 +89,14 @@ defmodule MastaniServer.Test.Query.Account.Basic do assert Map.equal?(Jason.decode!(results["cmsPassportString"]), @valid_rules) end - test "login user can get nil if cms_passport not exsit", ~m(user)a do + test "login user can get empty if cms_passport not exsit", ~m(user)a do user_conn = simu_conn(:user, user) variables = %{id: user.id} results = user_conn |> query_result(@query, variables, "user") - assert nil == results["cmsPassport"] - assert nil == results["cmsPassportString"] + assert %{} == results["cmsPassport"] + assert "{}" == results["cmsPassportString"] end @query """ @@ -159,15 +152,19 @@ defmodule MastaniServer.Test.Query.Account.Basic do nickname subscribedCommunitiesCount subscribedCommunities { - id - title + entries { + id + title + } + pageSize + totalCount } } } """ - test "gest user can get subscrubed community list and count", ~m(guest_conn user)a do + test "guest user can get subscrubed community list and count", ~m(guest_conn user)a do variables = %{id: user.id} - {:ok, communities} = db_insert_multi(:community, inner_page_size()) + {:ok, communities} = db_insert_multi(:community, page_size()) Enum.each( communities, @@ -175,7 +172,7 @@ defmodule MastaniServer.Test.Query.Account.Basic do ) results = guest_conn |> query_result(@query, variables, "user") - subscribed_communities = results["subscribedCommunities"] + subscribed_communities = results["subscribedCommunities"]["entries"] subscribed_communities_count = results["subscribedCommunitiesCount"] [community_1, community_2, community_3, community_x] = communities |> firstn_and_last(3) @@ -183,12 +180,12 @@ defmodule MastaniServer.Test.Query.Account.Basic do assert subscribed_communities |> Enum.any?(&(&1["id"] == to_string(community_2.id))) assert subscribed_communities |> Enum.any?(&(&1["id"] == to_string(community_3.id))) assert subscribed_communities |> Enum.any?(&(&1["id"] == to_string(community_x.id))) - assert subscribed_communities_count == inner_page_size() + assert subscribed_communities_count == page_size() end test "gest user can get subscrubed communities count of 20 at most", ~m(guest_conn user)a do variables = %{id: user.id} - {:ok, communities} = db_insert_multi(:community, inner_page_size() + 1) + {:ok, communities} = db_insert_multi(:community, page_size() + 1) Enum.each( communities, @@ -198,7 +195,8 @@ defmodule MastaniServer.Test.Query.Account.Basic do results = guest_conn |> query_result(@query, variables, "user") subscribed_communities = results["subscribedCommunities"] - assert length(subscribed_communities) == inner_page_size() + assert subscribed_communities["totalCount"] == page_size() + 1 + assert subscribed_communities["pageSize"] == page_size() end @query """ @@ -214,7 +212,7 @@ defmodule MastaniServer.Test.Query.Account.Basic do } } """ - test "gest user can get paged default subscrubed communities", ~m(guest_conn)a do + test "guest user can get paged default subscrubed communities", ~m(guest_conn)a do {:ok, _} = db_insert_multi(:community, 25) variables = %{filter: %{page: 1, size: 10}} @@ -248,4 +246,38 @@ defmodule MastaniServer.Test.Query.Account.Basic do assert @default_subscribed_communities == results["pageSize"] end end + + describe "[account session state]" do + @query """ + query { + sessionState { + isValid + user { + id + } + } + } + """ + test "guest user should get false sessionState", ~m(guest_conn)a do + results = guest_conn |> query_result(@query, %{}, "sessionState") + assert results["isValid"] == false + assert results["user"] == nil + end + + test "login user should get true sessionState", ~m(user)a do + user_conn = simu_conn(:user, user) + results = user_conn |> query_result(@query, %{}, "sessionState") + + assert results["isValid"] == true + assert results["user"] |> Map.get("id") == to_string(user.id) + end + + test "user with invalid token get false sessionState" do + user_conn = simu_conn(:invalid_token) + results = user_conn |> query_result(@query, %{}, "sessionState") + + assert results["isValid"] == false + assert results["user"] == nil + end + end end diff --git a/test/mastani_server_web/query/accounts/achievement_test.exs b/test/mastani_server_web/query/accounts/achievement_test.exs index 2e8b5ef43..cb1432e8f 100644 --- a/test/mastani_server_web/query/accounts/achievement_test.exs +++ b/test/mastani_server_web/query/accounts/achievement_test.exs @@ -1,7 +1,7 @@ defmodule MastaniServer.Test.Query.Account.Achievement do use MastaniServer.TestTools import Helper.Utils, only: [get_config: 2] - alias MastaniServer.Accounts + alias MastaniServer.{Accounts, CMS} alias Helper.ORM @@ -17,37 +17,113 @@ defmodule MastaniServer.Test.Query.Account.Achievement do {:ok, ~m(user_conn guest_conn user)a} end + describe "[account editable-communities]" do + @query """ + query($userId: ID, $filter: PagedFilter!) { + editableCommunities(userId: $userId, filter: $filter) { + entries { + id + logo + title + raw + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + test "can get user's empty editable communities list", ~m(guest_conn user)a do + variables = %{userId: user.id, filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "editableCommunities") + + assert results |> is_valid_pagination?(:empty) + end + + test "can get user's editable communities list when user is editor", ~m(guest_conn user)a do + {:ok, community} = db_insert(:community) + {:ok, community2} = db_insert(:community) + + title = "chief editor" + {:ok, _} = CMS.set_editor(community, title, user) + {:ok, _} = CMS.set_editor(community2, title, user) + + variables = %{userId: user.id, filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "editableCommunities") + + assert results["totalCount"] == 2 + assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(community.id))) + assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(community2.id))) + end + + @query """ + query { + user { + id + editableCommunities { + entries { + id + logo + title + raw + } + totalCount + } + } + } + """ + test "user can get own editable communities list", ~m(user)a do + user_conn = simu_conn(:user, user) + + {:ok, community} = db_insert(:community) + {:ok, community2} = db_insert(:community) + + title = "chief editor" + {:ok, _} = CMS.set_editor(community, title, user) + {:ok, _} = CMS.set_editor(community2, title, user) + + variables = %{filter: %{page: 1, size: 20}} + results = user_conn |> query_result(@query, variables, "user") + editable_communities = results["editableCommunities"] + + assert editable_communities["totalCount"] == 2 + assert editable_communities["entries"] |> Enum.any?(&(&1["id"] == to_string(community.id))) + assert editable_communities["entries"] |> Enum.any?(&(&1["id"] == to_string(community2.id))) + end + end + describe "[account follow achieveMent]" do @query """ query($id: ID!) { user(id: $id) { id + followersCount + followingsCount achievement { reputation - followersCount } } } """ - test "new you has no acheiveements", ~m(guest_conn user)a do - variables = %{id: user.id} - results = guest_conn |> query_result(@query, variables, "user") - - assert is_nil(results["achievement"]) - end - test "inc user's achievement after user got followed", ~m(guest_conn user)a do {:ok, user2} = db_insert(:user) + {:ok, user3} = db_insert(:user) + {:ok, user4} = db_insert(:user) + user2 |> Accounts.follow(user) + user |> Accounts.follow(user2) + user3 |> Accounts.follow(user2) + user3 |> Accounts.follow(user4) - variables = %{id: user.id} + variables = %{id: user2.id} results = guest_conn |> query_result(@query, variables, "user") - assert results["achievement"] |> Map.get("followersCount") == @follow_weight - assert results["achievement"] |> Map.get("reputation") == @follow_weight + assert results |> Map.get("followersCount") == 2 + assert results["achievement"] |> Map.get("reputation") == 2 * @follow_weight end - test "minus user's achievement after user get cancle followed", ~m(guest_conn user)a do + test "minus user's achievement after user get undo followed", ~m(guest_conn user)a do total_count = 10 {:ok, users} = db_insert_multi(:user, total_count) @@ -61,8 +137,7 @@ defmodule MastaniServer.Test.Query.Account.Achievement do variables = %{id: user.id} results = guest_conn |> query_result(@query, variables, "user") - assert results["achievement"] |> Map.get("followersCount") == - @follow_weight * (total_count - 1) + assert results |> Map.get("followersCount") == total_count - 1 assert results["achievement"] |> Map.get("reputation") == @follow_weight * (total_count - 1) end @@ -92,7 +167,7 @@ defmodule MastaniServer.Test.Query.Account.Achievement do variables = %{id: author_user_id} results = guest_conn |> query_result(@query, variables, "user") - assert results["achievement"] |> Map.get("contentsFavoritedCount") == @favorite_weight + assert results["achievement"] |> Map.get("contentsFavoritedCount") == 1 assert results["achievement"] |> Map.get("reputation") == @favorite_weight end @@ -114,11 +189,10 @@ defmodule MastaniServer.Test.Query.Account.Achievement do variables = %{id: author_user_id} results = guest_conn |> query_result(@query, variables, "user") - assert results["achievement"] |> Map.get("contentsFavoritedCount") == - @favorite_weight * (total_count - 1) + assert results["achievement"] |> Map.get("contentsFavoritedCount") == total_count - 1 assert results["achievement"] |> Map.get("reputation") == - @favorite_weight * (total_count - 1) + @favorite_weight * total_count - @favorite_weight end end end diff --git a/test/mastani_server_web/query/accounts/customization_test.exs b/test/mastani_server_web/query/accounts/customization_test.exs new file mode 100644 index 000000000..9c01f7e55 --- /dev/null +++ b/test/mastani_server_web/query/accounts/customization_test.exs @@ -0,0 +1,51 @@ +defmodule MastaniServer.Test.Query.Account.Customization do + use MastaniServer.TestTools + import Helper.Utils, only: [get_config: 2] + alias MastaniServer.{Accounts, CMS} + + alias Helper.ORM + + @default_customization get_config(:customization, :all) |> Enum.into(%{}) + + setup do + {:ok, user} = db_insert(:user) + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user, user) + + {:ok, ~m(user_conn guest_conn user)a} + end + + describe "[account customization]" do + @query """ + query { + user { + id + nickname + customization { + theme + communityChart + brainwashFree + bannerLayout + contentsLayout + contentDivider + markViewed + displayDensity + } + } + } + """ + test "user can have default customization configs", ~m(user_conn user)a do + results = user_conn |> query_result(@query, %{}, "user") + + assert results["id"] == to_string(user.id) + + assert results["customization"]["theme"] == @default_customization |> Map.get(:theme) + + assert results["customization"]["bannerLayout"] == + @default_customization |> Map.get(:banner_layout) + + assert results["customization"]["contentsLayout"] == + @default_customization |> Map.get(:contents_layout) + end + end +end diff --git a/test/mastani_server_web/query/accounts/fans_test.exs b/test/mastani_server_web/query/accounts/fans_test.exs index 610098b8d..102707f11 100644 --- a/test/mastani_server_web/query/accounts/fans_test.exs +++ b/test/mastani_server_web/query/accounts/fans_test.exs @@ -26,12 +26,14 @@ defmodule MastaniServer.Test.Query.Account.Fans do variables = %{filter: %{page: 1, size: 20}} {:ok, user2} = db_insert(:user) + {:ok, user3} = db_insert(:user) {:ok, _followeer} = user |> Accounts.follow(user2) + {:ok, _followeer} = user3 |> Accounts.follow(user2) user2_conn = simu_conn(:user, user2) results = user2_conn |> query_result(@query, variables, "pagedFollowers") - assert results |> Map.get("totalCount") == 1 + assert results |> Map.get("totalCount") == 2 assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(user.id))) end @@ -61,11 +63,15 @@ defmodule MastaniServer.Test.Query.Account.Fans do variables = %{filter: %{page: 1, size: 20}} {:ok, user2} = db_insert(:user) + {:ok, user3} = db_insert(:user) + {:ok, user4} = db_insert(:user) {:ok, _followeer} = user |> Accounts.follow(user2) + {:ok, _followeer} = user |> Accounts.follow(user3) + {:ok, _followeer} = user |> Accounts.follow(user4) results = user_conn |> query_result(@query, variables, "pagedFollowings") - assert results |> Map.get("totalCount") == 1 + assert results |> Map.get("totalCount") == 3 assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(user2.id))) end @@ -92,12 +98,13 @@ defmodule MastaniServer.Test.Query.Account.Fans do total_count = 15 {:ok, users} = db_insert_multi(:user, total_count) - Enum.each(users, fn cool_user -> - {:ok, _} = user |> Accounts.follow(cool_user) + Enum.each(users, fn other_user -> + {:ok, _} = other_user |> Accounts.follow(user) end) variables = %{id: user.id} resolts = user_conn |> query_result(@query, variables, "user") + assert resolts |> Map.get("followersCount") == total_count end @@ -141,9 +148,10 @@ defmodule MastaniServer.Test.Query.Account.Fans do resolts = user_conn |> query_result(@query, variables, "user") assert resolts |> Map.get("viewerHasFollowed") == false - {:ok, _} = user2 |> Accounts.follow(user) + {:ok, _} = user |> Accounts.follow(user2) variables = %{id: user2.id} resolts = user_conn |> query_result(@query, variables, "user") + assert resolts |> Map.get("viewerHasFollowed") == true end end diff --git a/test/mastani_server_web/query/accounts/favorite_category_test.exs b/test/mastani_server_web/query/accounts/favorite_category_test.exs index 1001a7195..c9524a09d 100644 --- a/test/mastani_server_web/query/accounts/favorite_category_test.exs +++ b/test/mastani_server_web/query/accounts/favorite_category_test.exs @@ -17,7 +17,7 @@ defmodule MastaniServer.Test.Query.Accounts.FavoriteCategory do describe "[Accounts FavoriteCategory]" do @query """ query($userId: ID, $filter: CommonPagedFilter!) { - listFavoriteCategories(userId: $userId, filter: $filter) { + favoriteCategories(userId: $userId, filter: $filter) { entries { id title @@ -35,7 +35,7 @@ defmodule MastaniServer.Test.Query.Accounts.FavoriteCategory do {:ok, _} = Accounts.create_favorite_category(user, %{title: test_category, private: false}) variables = %{filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "listFavoriteCategories") + results = user_conn |> query_result(@query, variables, "favoriteCategories") assert results |> is_valid_pagination? assert results["totalCount"] == 1 end @@ -47,7 +47,7 @@ defmodule MastaniServer.Test.Query.Accounts.FavoriteCategory do {:ok, _} = Accounts.create_favorite_category(user, %{title: test_category2, private: true}) variables = %{filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "listFavoriteCategories") + results = user_conn |> query_result(@query, variables, "favoriteCategories") assert results |> is_valid_pagination? assert results["totalCount"] == 2 end @@ -61,7 +61,7 @@ defmodule MastaniServer.Test.Query.Accounts.FavoriteCategory do {: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, "listFavoriteCategories") + results = guest_conn |> query_result(@query, variables, "favoriteCategories") assert results |> is_valid_pagination? assert results["entries"] |> Enum.any?(&(&1["title"] !== test_category2)) diff --git a/test/mastani_server_web/query/accounts/favorited_jobs_test.exs b/test/mastani_server_web/query/accounts/favorited_jobs_test.exs index 96940a8cb..7a63947d4 100644 --- a/test/mastani_server_web/query/accounts/favorited_jobs_test.exs +++ b/test/mastani_server_web/query/accounts/favorited_jobs_test.exs @@ -3,21 +3,22 @@ defmodule MastaniServer.Test.Query.Accounts.FavritedJobs do alias MastaniServer.CMS + @total_count 20 + setup do {:ok, user} = db_insert(:user) - total_count = 20 - {:ok, jobs} = db_insert_multi(:job, 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 total_count jobs)a} + {:ok, ~m(guest_conn user_conn user jobs)a} end describe "[accounts favorited jobs]" do @query """ query($filter: PagedFilter!) { - account { + user { id favoritedJobs(filter: $filter) { entries { @@ -29,7 +30,7 @@ defmodule MastaniServer.Test.Query.Accounts.FavritedJobs do } } """ - test "login user can get it's own favoritedJobs", ~m(user_conn user total_count jobs)a do + 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) @@ -37,10 +38,10 @@ defmodule MastaniServer.Test.Query.Accounts.FavritedJobs do 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, "account") + results = user_conn |> query_result(@query, variables, "user") # IO.inspect results, label: "hello" - assert results["favoritedJobs"] |> Map.get("totalCount") == total_count - assert results["favoritedJobsCount"] == total_count + assert results["favoritedJobs"] |> Map.get("totalCount") == @total_count + assert results["favoritedJobsCount"] == @total_count assert results["favoritedJobs"] |> Map.get("entries") @@ -48,8 +49,8 @@ defmodule MastaniServer.Test.Query.Accounts.FavritedJobs do end @query """ - query($userId: ID, $filter: PagedFilter!) { - favoritedJobs(userId: $userId, filter: $filter) { + query($userId: ID!, $categoryId: ID,$filter: PagedFilter!) { + favoritedJobs(userId: $userId, categoryId: $categoryId, filter: $filter) { entries { id } @@ -58,7 +59,7 @@ defmodule MastaniServer.Test.Query.Accounts.FavritedJobs do } """ test "other user can get other user's paged favoritedJobs", - ~m(user_conn guest_conn total_count jobs)a do + ~m(user_conn guest_conn jobs)a do {:ok, user} = db_insert(:user) Enum.each(jobs, fn job -> @@ -69,19 +70,56 @@ defmodule MastaniServer.Test.Query.Accounts.FavritedJobs do 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 + assert results["totalCount"] == @total_count + assert results2["totalCount"] == @total_count end - test "login user can get self paged favoritedJobs", ~m(user_conn user total_count jobs)a do + 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 = %{filter: %{page: 1, size: 20}} + variables = %{userId: user.id, filter: %{page: 1, size: 20}} + results = user_conn |> query_result(@query, variables, "favoritedJobs") + + assert results["totalCount"] == @total_count + end + + alias MastaniServer.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["totalCount"] == total_count + assert results == results2 end end end diff --git a/test/mastani_server_web/query/accounts/favorited_posts_test.exs b/test/mastani_server_web/query/accounts/favorited_posts_test.exs index 2be33ee67..bd80d3ce5 100644 --- a/test/mastani_server_web/query/accounts/favorited_posts_test.exs +++ b/test/mastani_server_web/query/accounts/favorited_posts_test.exs @@ -3,22 +3,23 @@ defmodule MastaniServer.Test.Query.Accounts.FavritedPosts do alias MastaniServer.CMS + @total_count 20 + setup do {:ok, user} = db_insert(:user) - total_count = 20 - {:ok, posts} = db_insert_multi(:post, total_count) + {: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 total_count posts)a} + {:ok, ~m(guest_conn user_conn user posts)a} end describe "[account favorited posts]" do @query """ query($filter: PagedFilter!) { - account { + user { id favoritedPosts(filter: $filter) { entries { @@ -30,7 +31,7 @@ defmodule MastaniServer.Test.Query.Accounts.FavritedPosts do } } """ - test "login user can get it's own favoritedPosts", ~m(user_conn user total_count posts)a do + 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) @@ -38,9 +39,9 @@ defmodule MastaniServer.Test.Query.Accounts.FavritedPosts do 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, "account") - assert results["favoritedPosts"] |> Map.get("totalCount") == total_count - assert results["favoritedPostsCount"] == total_count + 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") @@ -48,8 +49,8 @@ defmodule MastaniServer.Test.Query.Accounts.FavritedPosts do end @query """ - query($userId: ID, $filter: PagedFilter!) { - favoritedPosts(userId: $userId, filter: $filter) { + query($userId: ID!, $categoryId: ID, $filter: PagedFilter!) { + favoritedPosts(userId: $userId, categoryId: $categoryId, filter: $filter) { entries { id } @@ -58,7 +59,7 @@ defmodule MastaniServer.Test.Query.Accounts.FavritedPosts do } """ test "other user can get other user's paged favoritedPosts", - ~m(user_conn guest_conn total_count posts)a do + ~m(user_conn guest_conn posts)a do {:ok, user} = db_insert(:user) Enum.each(posts, fn post -> @@ -69,19 +70,45 @@ defmodule MastaniServer.Test.Query.Accounts.FavritedPosts do 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 + assert results["totalCount"] == @total_count + assert results2["totalCount"] == @total_count end - test "login user can get self paged favoritedPosts", ~m(user_conn user posts total_count)a do + alias MastaniServer.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) - variables = %{filter: %{page: 1, size: 20}} + 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["totalCount"] == total_count + assert results == results2 end end end diff --git a/test/mastani_server_web/query/accounts/favorited_repos_test.exs b/test/mastani_server_web/query/accounts/favorited_repos_test.exs new file mode 100644 index 000000000..a30f99c53 --- /dev/null +++ b/test/mastani_server_web/query/accounts/favorited_repos_test.exs @@ -0,0 +1,124 @@ +defmodule MastaniServer.Test.Query.Accounts.FavritedRepos do + use MastaniServer.TestTools + + alias MastaniServer.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 MastaniServer.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/mastani_server_web/query/accounts/favorited_videos_test.exs b/test/mastani_server_web/query/accounts/favorited_videos_test.exs new file mode 100644 index 000000000..315b59f41 --- /dev/null +++ b/test/mastani_server_web/query/accounts/favorited_videos_test.exs @@ -0,0 +1,114 @@ +defmodule MastaniServer.Test.Query.Accounts.FavritedVideos do + use MastaniServer.TestTools + + alias MastaniServer.CMS + + @total_count 20 + + setup do + {:ok, user} = db_insert(:user) + + {:ok, videos} = db_insert_multi(:video, @total_count) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user, user) + + {:ok, ~m(guest_conn user_conn user videos)a} + end + + describe "[account favorited videos]" do + @query """ + query($filter: PagedFilter!) { + user { + id + favoritedVideos(filter: $filter) { + entries { + id + } + totalCount + } + favoritedVideosCount + } + } + """ + test "login user can get it's own favoritedVideos", ~m(user_conn user videos)a do + Enum.each(videos, fn video -> + {:ok, _} = CMS.reaction(:video, :favorite, video.id, user) + end) + + random_id = videos |> 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["favoritedVideos"] |> Map.get("totalCount") == @total_count + assert results["favoritedVideosCount"] == @total_count + + assert results["favoritedVideos"] + |> Map.get("entries") + |> Enum.any?(&(&1["id"] == random_id)) + end + + @query """ + query($userId: ID!, $categoryId: ID, $filter: PagedFilter!) { + favoritedVideos(userId: $userId, categoryId: $categoryId, filter: $filter) { + entries { + id + } + totalCount + } + } + """ + test "other user can get other user's paged favoritedVideos", + ~m(user_conn guest_conn videos)a do + {:ok, user} = db_insert(:user) + + Enum.each(videos, fn video -> + {:ok, _} = CMS.reaction(:video, :favorite, video.id, user) + end) + + variables = %{userId: user.id, filter: %{page: 1, size: 20}} + results = user_conn |> query_result(@query, variables, "favoritedVideos") + results2 = guest_conn |> query_result(@query, variables, "favoritedVideos") + + assert results["totalCount"] == @total_count + assert results2["totalCount"] == @total_count + end + + alias MastaniServer.Accounts + + test "can get paged favoritedVideos on a spec category", ~m(user_conn guest_conn videos)a do + {:ok, user} = db_insert(:user) + + Enum.each(videos, fn video -> + {:ok, _} = CMS.reaction(:video, :favorite, video.id, user) + end) + + video1 = Enum.at(videos, 0) + video2 = Enum.at(videos, 1) + video3 = Enum.at(videos, 2) + video4 = Enum.at(videos, 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, :video, video1.id, category.id) + {:ok, _favorites_category} = Accounts.set_favorites(user, :video, video2.id, category.id) + {:ok, _favorites_category} = Accounts.set_favorites(user, :video, video3.id, category.id) + {:ok, _favorites_category} = Accounts.set_favorites(user, :video, video4.id, category2.id) + + variables = %{userId: user.id, categoryId: category.id, filter: %{page: 1, size: 20}} + results = user_conn |> query_result(@query, variables, "favoritedVideos") + results2 = guest_conn |> query_result(@query, variables, "favoritedVideos") + + assert results["totalCount"] == 3 + assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(video1.id))) + assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(video2.id))) + assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(video3.id))) + + assert results == results2 + end + end +end diff --git a/test/mastani_server_web/query/accounts/messages_test.exs b/test/mastani_server_web/query/accounts/messages_test.exs index 1c2a2f873..27b5685e1 100644 --- a/test/mastani_server_web/query/accounts/messages_test.exs +++ b/test/mastani_server_web/query/accounts/messages_test.exs @@ -14,7 +14,7 @@ defmodule MastaniServer.Test.Query.Accounts.Messages do describe "[account messages queries]" do @query """ query { - account { + user { id mailBox { hasMail @@ -29,7 +29,7 @@ defmodule MastaniServer.Test.Query.Accounts.Messages do {:ok, user} = db_insert(:user) user_conn = simu_conn(:user, user) - result = user_conn |> query_result(@query, %{}, "account") + result = user_conn |> query_result(@query, %{}, "user") mail_box = result["mailBox"] assert mail_box["hasMail"] == false assert mail_box["totalCount"] == 0 @@ -39,7 +39,7 @@ defmodule MastaniServer.Test.Query.Accounts.Messages do mock_mentions_for(user, 2) mock_notifications_for(user, 18) - result = user_conn |> query_result(@query, %{}, "account") + result = user_conn |> query_result(@query, %{}, "user") mail_box = result["mailBox"] assert mail_box["hasMail"] == true assert mail_box["totalCount"] == 20 @@ -55,7 +55,7 @@ defmodule MastaniServer.Test.Query.Accounts.Messages do @query """ query($filter: MessagesFilter!) { - account { + user { id mentions(filter: $filter) { entries { @@ -90,14 +90,14 @@ defmodule MastaniServer.Test.Query.Accounts.Messages do user_conn = simu_conn(:user, user) variables = %{filter: %{page: 1, size: 20, read: false}} - result = user_conn |> query_result(@query, variables, "account") + result = user_conn |> query_result(@query, variables, "user") mentions = result["mentions"] assert mentions["totalCount"] == 0 mock_mentions_for(user, 3) variables = %{filter: %{page: 1, size: 20, read: false}} - result = user_conn |> query_result(@query, variables, "account") + result = user_conn |> query_result(@query, variables, "user") mentions = result["mentions"] assert mentions["totalCount"] == 3 @@ -109,14 +109,14 @@ defmodule MastaniServer.Test.Query.Accounts.Messages do user_conn = simu_conn(:user, user) variables = %{filter: %{page: 1, size: 20, read: false}} - result = user_conn |> query_result(@query, variables, "account") + result = user_conn |> query_result(@query, variables, "user") notifications = result["notifications"] assert notifications["totalCount"] == 0 mock_notifications_for(user, 3) variables = %{filter: %{page: 1, size: 20, read: false}} - result = user_conn |> query_result(@query, variables, "account") + result = user_conn |> query_result(@query, variables, "user") notifications = result["notifications"] assert notifications["totalCount"] == 3 @@ -128,14 +128,14 @@ defmodule MastaniServer.Test.Query.Accounts.Messages do user_conn = simu_conn(:user, user) variables = %{filter: %{page: 1, size: 20, read: false}} - result = user_conn |> query_result(@query, variables, "account") + result = user_conn |> query_result(@query, variables, "user") notifications = result["sysNotifications"] assert notifications["totalCount"] == 0 mock_sys_notification(5) variables = %{filter: %{page: 1, size: 20, read: false}} - result = user_conn |> query_result(@query, variables, "account") + result = user_conn |> query_result(@query, variables, "user") notifications = result["sysNotifications"] assert notifications["totalCount"] == 5 diff --git a/test/mastani_server_web/query/accounts/published_comments_test.exs b/test/mastani_server_web/query/accounts/published_comments_test.exs new file mode 100644 index 000000000..0adfdc79e --- /dev/null +++ b/test/mastani_server_web/query/accounts/published_comments_test.exs @@ -0,0 +1,200 @@ +defmodule MastaniServer.Test.Query.Accounts.PublishedComments do + use MastaniServer.TestTools + + alias MastaniServer.CMS + + @publish_count 10 + + setup do + {:ok, user} = db_insert(:user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user, user) + + {:ok, ~m(guest_conn user_conn user)a} + end + + describe "[account published comments on post]" do + @query """ + query($userId: ID!, $filter: PagedFilter!) { + publishedPostComments(userId: $userId, filter: $filter) { + entries { + id + body + author { + id + } + post { + id + title + } + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + test "user can get paged published comments on post", ~m(guest_conn user)a do + {:ok, post} = db_insert(:post) + + pub_comments = + Enum.reduce(1..@publish_count, [], fn _, acc -> + body = "this is a test comment" + {:ok, comment} = CMS.create_comment(:post, post.id, body, user) + acc ++ [comment] + end) + + random_comment_id = pub_comments |> Enum.random() |> Map.get(:id) |> to_string + + variables = %{userId: user.id, filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "publishedPostComments") + + assert results |> is_valid_pagination? + assert results["totalCount"] == @publish_count + + assert results["entries"] |> Enum.all?(&(&1["post"]["id"] == to_string(post.id))) + assert results["entries"] |> Enum.all?(&(&1["author"]["id"] == to_string(user.id))) + assert results["entries"] |> Enum.any?(&(&1["id"] == random_comment_id)) + end + end + + describe "[account published comments on job]" do + @query """ + query($userId: ID!, $filter: PagedFilter!) { + publishedJobComments(userId: $userId, filter: $filter) { + entries { + id + body + author { + id + } + job { + id + title + } + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + test "user can get paged published comments on job", ~m(guest_conn user)a do + {:ok, job} = db_insert(:job) + + pub_comments = + Enum.reduce(1..@publish_count, [], fn _, acc -> + body = "this is a test comment" + {:ok, comment} = CMS.create_comment(:job, job.id, body, user) + acc ++ [comment] + end) + + random_comment_id = pub_comments |> Enum.random() |> Map.get(:id) |> to_string + + variables = %{userId: user.id, filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "publishedJobComments") + + assert results |> is_valid_pagination? + assert results["totalCount"] == @publish_count + + assert results["entries"] |> Enum.all?(&(&1["job"]["id"] == to_string(job.id))) + assert results["entries"] |> Enum.all?(&(&1["author"]["id"] == to_string(user.id))) + assert results["entries"] |> Enum.any?(&(&1["id"] == random_comment_id)) + end + end + + describe "[account published comments on video]" do + @query """ + query($userId: ID!, $filter: PagedFilter!) { + publishedVideoComments(userId: $userId, filter: $filter) { + entries { + id + body + author { + id + } + video { + id + title + } + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + test "user can get paged published comments on video", ~m(guest_conn user)a do + {:ok, video} = db_insert(:video) + + pub_comments = + Enum.reduce(1..@publish_count, [], fn _, acc -> + body = "this is a test comment" + {:ok, comment} = CMS.create_comment(:video, video.id, body, user) + acc ++ [comment] + end) + + random_comment_id = pub_comments |> Enum.random() |> Map.get(:id) |> to_string + + variables = %{userId: user.id, filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "publishedVideoComments") + + assert results |> is_valid_pagination? + assert results["totalCount"] == @publish_count + + assert results["entries"] |> Enum.all?(&(&1["video"]["id"] == to_string(video.id))) + assert results["entries"] |> Enum.all?(&(&1["author"]["id"] == to_string(user.id))) + assert results["entries"] |> Enum.any?(&(&1["id"] == random_comment_id)) + end + end + + describe "[account published comments on repo]" do + @query """ + query($userId: ID!, $filter: PagedFilter!) { + publishedRepoComments(userId: $userId, filter: $filter) { + entries { + id + body + author { + id + } + repo { + id + title + } + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + test "user can get paged published comments on repo", ~m(guest_conn user)a do + {:ok, repo} = db_insert(:repo) + + pub_comments = + Enum.reduce(1..@publish_count, [], fn _, acc -> + body = "this is a test comment" + {:ok, comment} = CMS.create_comment(:repo, repo.id, body, user) + acc ++ [comment] + end) + + random_comment_id = pub_comments |> Enum.random() |> Map.get(:id) |> to_string + + variables = %{userId: user.id, filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "publishedRepoComments") + + assert results |> is_valid_pagination? + assert results["totalCount"] == @publish_count + + assert results["entries"] |> Enum.all?(&(&1["repo"]["id"] == to_string(repo.id))) + assert results["entries"] |> Enum.all?(&(&1["author"]["id"] == to_string(user.id))) + assert results["entries"] |> Enum.any?(&(&1["id"] == random_comment_id)) + end + end +end diff --git a/test/mastani_server_web/query/accounts/published_contents_test.exs b/test/mastani_server_web/query/accounts/published_contents_test.exs new file mode 100644 index 000000000..7057a7d1d --- /dev/null +++ b/test/mastani_server_web/query/accounts/published_contents_test.exs @@ -0,0 +1,173 @@ +defmodule MastaniServer.Test.Query.Accounts.PublishedContents do + use MastaniServer.TestTools + + alias MastaniServer.CMS + + @publish_count 10 + + setup do + {:ok, user} = db_insert(:user) + {:ok, community} = db_insert(:community) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user, user) + + {:ok, ~m(guest_conn user_conn user community)a} + end + + describe "[account published posts]" do + @query """ + query($userId: ID!, $filter: PagedFilter!) { + publishedPosts(userId: $userId, filter: $filter) { + entries { + id + author { + id + } + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + test "user can get paged published posts", ~m(guest_conn user community)a do + pub_posts = + Enum.reduce(1..@publish_count, [], fn _, acc -> + post_attrs = mock_attrs(:post, %{community_id: community.id}) + {:ok, post} = CMS.create_content(community, :post, post_attrs, user) + + acc ++ [post] + end) + + random_post_id = pub_posts |> Enum.random() |> Map.get(:id) |> to_string + + variables = %{userId: user.id, filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "publishedPosts") + + assert results |> is_valid_pagination? + assert results["totalCount"] == @publish_count + + assert results["entries"] |> Enum.all?(&(&1["author"]["id"] == to_string(user.id))) + assert results["entries"] |> Enum.any?(&(&1["id"] == random_post_id)) + end + end + + describe "[account published jobs]" do + @query """ + query($userId: ID!, $filter: PagedFilter!) { + publishedJobs(userId: $userId, filter: $filter) { + entries { + id + author { + id + } + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + test "user can get paged published jobs", ~m(guest_conn user community)a do + pub_jobs = + Enum.reduce(1..@publish_count, [], fn _, acc -> + job_attrs = mock_attrs(:job, %{community_id: community.id}) + {:ok, job} = CMS.create_content(community, :job, job_attrs, user) + + acc ++ [job] + end) + + random_job_id = pub_jobs |> Enum.random() |> Map.get(:id) |> to_string + + variables = %{userId: user.id, filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "publishedJobs") + + assert results |> is_valid_pagination? + assert results["totalCount"] == @publish_count + + assert results["entries"] |> Enum.all?(&(&1["author"]["id"] == to_string(user.id))) + assert results["entries"] |> Enum.any?(&(&1["id"] == random_job_id)) + end + end + + describe "[account published videos]" do + @query """ + query($userId: ID!, $filter: PagedFilter!) { + publishedVideos(userId: $userId, filter: $filter) { + entries { + id + author { + id + } + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + test "user can get paged published videos", ~m(guest_conn user community)a do + pub_videos = + Enum.reduce(1..@publish_count, [], fn _, acc -> + video_attrs = mock_attrs(:video, %{community_id: community.id}) + {:ok, video} = CMS.create_content(community, :video, video_attrs, user) + + acc ++ [video] + end) + + random_video_id = pub_videos |> Enum.random() |> Map.get(:id) |> to_string + + variables = %{userId: user.id, filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "publishedVideos") + + assert results |> is_valid_pagination? + assert results["totalCount"] == @publish_count + + assert results["entries"] |> Enum.all?(&(&1["author"]["id"] == to_string(user.id))) + assert results["entries"] |> Enum.any?(&(&1["id"] == random_video_id)) + end + end + + describe "[account published repos]" do + @query """ + query($userId: ID!, $filter: PagedFilter!) { + publishedRepos(userId: $userId, filter: $filter) { + entries { + id + author { + id + } + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + test "user can get paged published repos", ~m(guest_conn user community)a do + pub_repos = + Enum.reduce(1..@publish_count, [], fn _, acc -> + repo_attrs = mock_attrs(:repo, %{community_id: community.id}) + {:ok, repo} = CMS.create_content(community, :repo, repo_attrs, user) + + acc ++ [repo] + end) + + random_repo_id = pub_repos |> Enum.random() |> Map.get(:id) |> to_string + + variables = %{userId: user.id, filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "publishedRepos") + + assert results |> is_valid_pagination? + assert results["totalCount"] == @publish_count + + assert results["entries"] |> Enum.all?(&(&1["author"]["id"] == to_string(user.id))) + assert results["entries"] |> Enum.any?(&(&1["id"] == random_repo_id)) + end + end +end diff --git a/test/mastani_server_web/query/accounts/stared_contents_test.exs b/test/mastani_server_web/query/accounts/stared_contents_test.exs new file mode 100644 index 000000000..646d1d07f --- /dev/null +++ b/test/mastani_server_web/query/accounts/stared_contents_test.exs @@ -0,0 +1,199 @@ +defmodule MastaniServer.Test.Query.Accounts.StaredContents do + use MastaniServer.TestTools + + alias MastaniServer.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) + {:ok, videos} = db_insert_multi(:video, @total_count) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user, user) + + {:ok, ~m(guest_conn user_conn user posts jobs videos)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") + # IO.inspect results, label: "hello" + 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") + # IO.inspect results, label: "hello" + 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 + + describe "[accounts stared videos]" do + @query """ + query($filter: PagedFilter!) { + user { + id + staredVideos(filter: $filter) { + entries { + id + } + totalCount + } + staredVideosCount + } + } + """ + test "login user can get it's own staredVideos", ~m(user_conn user videos)a do + Enum.each(videos, fn video -> + {:ok, _} = CMS.reaction(:video, :star, video.id, user) + end) + + random_id = videos |> Enum.shuffle() |> List.first() |> Map.get(:id) |> to_string + + variables = %{filter: %{page: 1, size: 20}} + results = user_conn |> query_result(@query, variables, "user") + # IO.inspect results, label: "hello" + assert results["staredVideos"] |> Map.get("totalCount") == @total_count + assert results["staredVideosCount"] == @total_count + + assert results["staredVideos"] + |> Map.get("entries") + |> Enum.any?(&(&1["id"] == random_id)) + end + + @query """ + query($userId: ID, $filter: PagedFilter!) { + staredVideos(userId: $userId, filter: $filter) { + entries { + id + } + totalCount + } + } + """ + test "other user can get other user's paged staredVideos", + ~m(user_conn guest_conn videos)a do + {:ok, user} = db_insert(:user) + + Enum.each(videos, fn video -> + {:ok, _} = CMS.reaction(:video, :star, video.id, user) + end) + + variables = %{userId: user.id, filter: %{page: 1, size: 20}} + results = user_conn |> query_result(@query, variables, "staredVideos") + results2 = guest_conn |> query_result(@query, variables, "staredVideos") + + assert results["totalCount"] == @total_count + assert results2["totalCount"] == @total_count + end + end +end diff --git a/test/mastani_server_web/query/cms/cheatsheet_test.exs b/test/mastani_server_web/query/cms/cheatsheet_test.exs new file mode 100644 index 000000000..7982172c8 --- /dev/null +++ b/test/mastani_server_web/query/cms/cheatsheet_test.exs @@ -0,0 +1,55 @@ +defmodule MastaniServer.Test.Query.Cheatsheet do + use MastaniServer.TestTools + + alias MastaniServer.CMS + + setup do + {:ok, community} = db_insert(:community) + + cheatsheet_attrs = mock_attrs(:cheatsheet, %{community_id: community.id}) + {:ok, cheatsheet} = CMS.sync_github_content(community, :cheatsheet, cheatsheet_attrs) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user) + + {:ok, ~m(user_conn guest_conn community cheatsheet)a} + end + + @query """ + query($community: String!) { + cheatsheet(community: $community) { + id + readme + contributors { + avatar + nickname + } + } + } + """ + test "basic graphql query on cheatsheet", ~m(guest_conn community cheatsheet)a do + variables = %{community: community.raw} + results = guest_conn |> query_result(@query, variables, "cheatsheet") + + assert results["id"] == to_string(cheatsheet.id) + assert is_valid_kv?(results, "readme", :string) + assert results["contributors"] |> length !== 0 + end + + @query """ + query($community: String!) { + cheatsheet(community: $community) { + views + } + } + """ + test "views should +1 after query the cheatsheet", ~m(guest_conn community)a do + variables = %{community: community.raw} + views_1 = guest_conn |> query_result(@query, variables, "cheatsheet") |> Map.get("views") + + variables = %{community: community.raw} + views_2 = guest_conn |> query_result(@query, variables, "cheatsheet") |> Map.get("views") + + assert views_2 == views_1 + 1 + end +end diff --git a/test/mastani_server_web/query/cms/cms_counts_test.exs b/test/mastani_server_web/query/cms/cms_counts_test.exs new file mode 100644 index 000000000..00112a116 --- /dev/null +++ b/test/mastani_server_web/query/cms/cms_counts_test.exs @@ -0,0 +1,124 @@ +defmodule MastaniServer.Test.Query.CMS.ContentCounts do + use MastaniServer.TestTools + + # alias MastaniServer.Accounts.User + alias MastaniServer.CMS + # alias CMS.{Community, Thread, Category} + + setup do + guest_conn = simu_conn(:guest) + {:ok, community} = db_insert(:community) + {:ok, user} = db_insert(:user) + + {:ok, ~m(guest_conn community user)a} + end + + describe "[cms contents count]" do + @query """ + query($id: ID) { + community(id: $id) { + id + title + postsCount + } + } + """ + test "community have valid posts_count", ~m(guest_conn community user)a do + variables = %{id: community.id} + results = guest_conn |> query_result(@query, variables, "community") + assert results["postsCount"] == 0 + + count = Enum.random(1..20) + + Enum.reduce(1..count, [], fn _, acc -> + post_attrs = mock_attrs(:post, %{community_id: community.id}) + {:ok, post} = CMS.create_content(community, :post, post_attrs, user) + acc ++ [post] + end) + + results = guest_conn |> query_result(@query, variables, "community") + assert results["postsCount"] == count + end + + @query """ + query($id: ID) { + community(id: $id) { + id + title + jobsCount + } + } + """ + test "community have valid jobs_count", ~m(guest_conn community user)a do + variables = %{id: community.id} + results = guest_conn |> query_result(@query, variables, "community") + assert results["jobsCount"] == 0 + + count = Enum.random(1..20) + + Enum.reduce(1..count, [], fn _, acc -> + job_attrs = mock_attrs(:job, %{community_id: community.id}) + {:ok, job} = CMS.create_content(community, :job, job_attrs, user) + + acc ++ [job] + end) + + results = guest_conn |> query_result(@query, variables, "community") + assert results["jobsCount"] == count + end + + @query """ + query($id: ID) { + community(id: $id) { + id + title + videosCount + } + } + """ + test "community have valid videos_count", ~m(guest_conn community user)a do + variables = %{id: community.id} + results = guest_conn |> query_result(@query, variables, "community") + assert results["videosCount"] == 0 + + count = Enum.random(1..20) + + Enum.reduce(1..count, [], fn _, acc -> + video_attrs = mock_attrs(:video, %{community_id: community.id}) + {:ok, video} = CMS.create_content(community, :video, video_attrs, user) + + acc ++ [video] + end) + + results = guest_conn |> query_result(@query, variables, "community") + assert results["videosCount"] == count + end + + @query """ + query($id: ID) { + community(id: $id) { + id + title + reposCount + } + } + """ + test "community have valid repos_count", ~m(guest_conn community user)a do + variables = %{id: community.id} + results = guest_conn |> query_result(@query, variables, "community") + assert results["reposCount"] == 0 + + count = Enum.random(1..20) + + Enum.reduce(1..count, [], fn _, acc -> + repo_attrs = mock_attrs(:repo, %{community_id: community.id}) + {:ok, repo} = CMS.create_content(community, :repo, repo_attrs, user) + + acc ++ [repo] + end) + + results = guest_conn |> query_result(@query, variables, "community") + assert results["reposCount"] == count + end + end +end diff --git a/test/mastani_server_web/query/cms/cms_geo_test.exs b/test/mastani_server_web/query/cms/cms_geo_test.exs new file mode 100644 index 000000000..8ea37a57b --- /dev/null +++ b/test/mastani_server_web/query/cms/cms_geo_test.exs @@ -0,0 +1,40 @@ +defmodule MastaniServer.Test.Query.CMS.GEO do + use MastaniServer.TestTools + + alias MastaniServer.CMS + + setup do + guest_conn = simu_conn(:guest) + {:ok, community} = db_insert(:community) + {:ok, user} = db_insert(:user) + + {:ok, ~m(guest_conn community user)a} + end + + @query """ + query($id: ID) { + communityGeoInfo(id: $id) { + city + long + lant + value + } + } + """ + test "empty community should get empty geo info", ~m(guest_conn community)a do + variables = %{id: community.id} + results = guest_conn |> query_result(@query, variables, "communityGeoInfo") + + assert results == [] + end + + test "community should get geo info after subscribe", ~m(guest_conn community user)a do + {:ok, _record} = CMS.subscribe_community(community, user) + + variables = %{id: community.id} + results = guest_conn |> query_result(@query, variables, "communityGeoInfo") + + assert results |> List.first() |> Map.get("value") == 1 + assert results |> List.first() |> Map.get("city") == "成都" + end +end diff --git a/test/mastani_server_web/query/cms/cms_test.exs b/test/mastani_server_web/query/cms/cms_test.exs index 667200683..fb50439c6 100644 --- a/test/mastani_server_web/query/cms/cms_test.exs +++ b/test/mastani_server_web/query/cms/cms_test.exs @@ -246,8 +246,8 @@ defmodule MastaniServer.Test.Query.CMS.Basic do end @query """ - query($communityId: ID, $thread: CmsThread!) { - partialTags(communityId: $communityId, thread: $thread) { + query($communityId: ID, $thread: CmsThread!, $topic: String ) { + partialTags(communityId: $communityId, thread: $thread, topic: $topic) { id title color @@ -260,7 +260,7 @@ defmodule MastaniServer.Test.Query.CMS.Basic do } } """ - test "guest user can get partial tags by communityId", ~m(guest_conn community)a do + test "guest user can get partial tags by communityId and thread", ~m(guest_conn community)a do {:ok, tag} = db_insert(:tag, %{thread: "post", community: community}) {:ok, tag2} = db_insert(:tag, %{thread: "job", community: community}) @@ -272,6 +272,16 @@ defmodule MastaniServer.Test.Query.CMS.Basic do assert results |> Enum.any?(&(&1["id"] != to_string(tag2.id))) end + test "user can get partial tags by default index topic", ~m(guest_conn community user)a do + valid_attrs = mock_attrs(:tag, %{community_id: community.id}) + {:ok, _tag} = CMS.create_tag(:post, valid_attrs, %User{id: user.id}) + + variables = %{thread: "POST", communityId: community.id, topic: "index"} + results = guest_conn |> query_result(@query, variables, "partialTags") + + assert results |> length == 1 + end + @query """ query($community: String, $thread: CmsThread!) { partialTags(community: $community, thread: $thread) { diff --git a/test/mastani_server_web/query/cms/job_comment_test.exs b/test/mastani_server_web/query/cms/job_comment_test.exs index f0a8777e8..b763b1369 100644 --- a/test/mastani_server_web/query/cms/job_comment_test.exs +++ b/test/mastani_server_web/query/cms/job_comment_test.exs @@ -13,14 +13,166 @@ defmodule MastaniServer.Test.Query.JobComment do {:ok, ~m(user_conn guest_conn job user)a} end + describe "[job dataloader comment]" do + @query """ + query($filter: PagedArticleFilter) { + pagedJobs(filter: $filter) { + entries { + id + title + commentsParticipators(filter: { first: 5 }) { + id + nickname + } + pagedCommentsParticipators { + entries { + id + } + totalCount + } + commentsCount + } + totalCount + } + } + """ + test "can get comments participators of a job", ~m(user guest_conn)a do + {:ok, user2} = db_insert(:user) + + {:ok, community} = db_insert(:community) + {:ok, job} = CMS.create_content(community, :job, mock_attrs(:job), user) + + variables = %{thread: "JOB", filter: %{community: community.raw}} + guest_conn |> query_result(@query, variables, "pagedJobs") + + body = "this is a test comment" + assert {:ok, _comment} = CMS.create_comment(:job, job.id, body, user) + assert {:ok, _comment} = CMS.create_comment(:job, job.id, body, user) + + assert {:ok, _comment} = CMS.create_comment(:job, job.id, body, user2) + + variables = %{filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedJobs") + + comments_count = results["entries"] |> List.first() |> Map.get("commentsCount") + + assert comments_count == 3 + end + + test "can get comments participators of a job with multi user", ~m(user guest_conn)a do + body = "this is a test comment" + {:ok, community} = db_insert(:community) + {:ok, job1} = CMS.create_content(community, :job, mock_attrs(:job), user) + {:ok, job2} = CMS.create_content(community, :job, mock_attrs(:job), user) + + {:ok, users_list} = db_insert_multi(:user, 10) + {:ok, users_list2} = db_insert_multi(:user, 10) + + Enum.each( + users_list, + &CMS.create_comment(:job, job1.id, body, &1) + ) + + Enum.each( + users_list2, + &CMS.create_comment(:job, job2.id, body, &1) + ) + + variables = %{thread: "JOB", filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedJobs") + + assert results["entries"] |> List.first() |> Map.get("commentsParticipators") |> length == 5 + assert results["entries"] |> List.last() |> Map.get("commentsParticipators") |> length == 5 + end + + test "can get paged commetns participators of a job", ~m(user guest_conn)a do + body = "this is a test comment" + + {:ok, community} = db_insert(:community) + {:ok, job} = CMS.create_content(community, :job, mock_attrs(:job), user) + {:ok, users_list} = db_insert_multi(:user, 10) + + Enum.each( + users_list, + &CMS.create_comment(:job, job.id, body, &1) + ) + + variables = %{filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedJobs") + participators = results["entries"] |> List.first() |> Map.get("pagedCommentsParticipators") + + assert participators["totalCount"] == 10 + end + end + + @query """ + query($id: ID!, $thread: CmsThread, $filter: PagedFilter!) { + pagedCommentsParticipators(id: $id, thread: $thread, filter: $filter) { + entries { + id + nickname + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + test "can get job's paged commetns participators", ~m(user guest_conn)a do + body = "this is a test comment" + + {:ok, community} = db_insert(:community) + {:ok, job} = CMS.create_content(community, :job, mock_attrs(:job), user) + {:ok, users_list} = db_insert_multi(:user, 10) + + Enum.each( + users_list, + &CMS.create_comment(:job, job.id, body, &1) + ) + + variables = %{id: job.id, thread: "JOB", filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "pagedCommentsParticipators") + assert results |> is_valid_pagination?() + + assert results["totalCount"] == 10 + end + # TODO: user can get specific user's replies :list_replies describe "[job comment]" do + @query """ + query($filter: PagedArticleFilter) { + pagedJobs(filter: $filter) { + entries { + id + title + commentsCount + } + totalCount + } + } + """ + test "can get comments info in paged jobs", ~m(user guest_conn)a do + body = "this is a test comment" + + {:ok, community} = db_insert(:community) + {:ok, job} = CMS.create_content(community, :job, mock_attrs(:job), user) + {:ok, _comment} = CMS.create_comment(:job, job.id, body, user) + + variables = %{filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedJobs") + + assert results["entries"] |> List.first() |> Map.get("commentsCount") == 1 + end + @query """ query($thread: CmsThread, $id: ID!, $filter: CommentsFilter!) { - comments(thread: $thread, id: $id, filter: $filter) { + pagedComments(thread: $thread, id: $id, filter: $filter) { entries { id body + likesCount + dislikesCount } totalPages totalCount @@ -39,15 +191,133 @@ defmodule MastaniServer.Test.Query.JobComment do end) variables = %{thread: "JOB", id: job.id, filter: %{page: 1, size: 10}} - results = guest_conn |> query_result(@query, variables, "comments") + results = guest_conn |> query_result(@query, variables, "pagedComments") assert results |> is_valid_pagination? assert results["totalCount"] == 30 end + test "MOST_LIKES filter should work", ~m(guest_conn job user)a do + body = "test comment" + + comments = + Enum.reduce(1..10, [], fn _, acc -> + {:ok, value} = CMS.create_comment(:job, job.id, 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(:job_comment, comment_3.id, user_1) + {:ok, _} = CMS.like_comment(:job_comment, comment_3.id, user_2) + {:ok, _} = CMS.like_comment(:job_comment, comment_3.id, user_3) + {:ok, _} = CMS.like_comment(:job_comment, comment_3.id, user_4) + {:ok, _} = CMS.like_comment(:job_comment, comment_3.id, user_5) + + {:ok, _} = CMS.like_comment(:job_comment, comment_1.id, user_1) + {:ok, _} = CMS.like_comment(:job_comment, comment_1.id, user_2) + {:ok, _} = CMS.like_comment(:job_comment, comment_1.id, user_3) + {:ok, _} = CMS.like_comment(:job_comment, comment_1.id, user_4) + + variables = %{ + thread: "JOB", + id: job.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 + + test "MOST_DISLIKES filter should work", ~m(guest_conn job user)a do + body = "test comment" + + comments = + Enum.reduce(1..10, [], fn _, acc -> + {:ok, value} = CMS.create_comment(:job, job.id, 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.dislike_comment(:job_comment, comment_3.id, user_1) + {:ok, _} = CMS.dislike_comment(:job_comment, comment_3.id, user_2) + {:ok, _} = CMS.dislike_comment(:job_comment, comment_3.id, user_3) + {:ok, _} = CMS.dislike_comment(:job_comment, comment_3.id, user_4) + {:ok, _} = CMS.dislike_comment(:job_comment, comment_3.id, user_5) + + {:ok, _} = CMS.dislike_comment(:job_comment, comment_1.id, user_1) + {:ok, _} = CMS.dislike_comment(:job_comment, comment_1.id, user_2) + {:ok, _} = CMS.dislike_comment(:job_comment, comment_1.id, user_3) + {:ok, _} = CMS.dislike_comment(:job_comment, comment_1.id, user_4) + + variables = %{ + thread: "JOB", + id: job.id, + filter: %{page: 1, size: 10, sort: "MOST_DISLIKES"} + } + + 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("dislikesCount") == 5 + + assert entries |> Enum.at(1) |> Map.get("id") == to_string(comment_1.id) + assert entries |> Enum.at(1) |> Map.get("dislikesCount") == 4 + end + + @query """ + query($thread: CmsThread, $id: ID!, $filter: CommentsFilter!) { + pagedComments(thread: $thread, id: $id, filter: $filter) { + entries { + id + viewerHasLiked + } + } + } + """ + test "login user can get hasLiked feedBack", ~m(user_conn job user)a do + body = "test comment" + + {:ok, comment} = CMS.create_comment(:job, job.id, body, user) + + {:ok, _like} = CMS.like_comment(:job_comment, comment.id, user) + + variables = %{thread: "JOB", id: job.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($thread: CmsThread, $id: ID!, $filter: PagedFilter!) { - comments(thread: $thread, id: $id, filter: $filter) { + pagedComments(thread: $thread, id: $id, filter: $filter) { entries { id body @@ -72,7 +342,7 @@ defmodule MastaniServer.Test.Query.JobComment do {:ok, reply} = CMS.reply_comment(:job, comment.id, "reply body", user) variables = %{thread: "JOB", id: job.id, filter: %{page: 1, size: 10}} - results = guest_conn |> query_result(@query, variables, "comments") + results = guest_conn |> query_result(@query, variables, "pagedComments") found = results["entries"] |> Enum.filter(&(&1["id"] == to_string(comment.id))) |> List.first() diff --git a/test/mastani_server_web/query/cms/job_test.exs b/test/mastani_server_web/query/cms/job_test.exs index 9ecc46230..fd7cc23a5 100644 --- a/test/mastani_server_web/query/cms/job_test.exs +++ b/test/mastani_server_web/query/cms/job_test.exs @@ -38,85 +38,79 @@ defmodule MastaniServer.Test.Query.Job do assert is_valid_kv?(results, "body", :string) end - # @query """ - # query($id: ID!) { - # job(id: $id) { - # id - # favoritedUsers { - # nickname - # id - # } - # } - # } - # """ - # test "post 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 Post($id: ID!) { - # post(id: $id) { - # views - # } - # } - # """ - # test "views should +1 after query the post", ~m(user_conn post)a do - # variables = %{id: post.id} - # views_1 = user_conn |> query_result(@query, variables, "post") |> Map.get("views") - - # variables = %{id: post.id} - # views_2 = user_conn |> query_result(@query, variables, "post") |> Map.get("views") - # assert views_2 == views_1 + 1 - # end - - # @query """ - # query Post($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) - # end - - # @query """ - # query Post($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) - # 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 MastaniServer.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/mastani_server_web/query/cms/job_viewer_test.exs b/test/mastani_server_web/query/cms/job_viewer_test.exs new file mode 100644 index 000000000..8d9579e1c --- /dev/null +++ b/test/mastani_server_web/query/cms/job_viewer_test.exs @@ -0,0 +1,109 @@ +defmodule MastaniServer.Test.Query.JobViewer do + use MastaniServer.TestTools + + alias Helper.ORM + alias MastaniServer.CMS + + setup do + {:ok, community} = db_insert(:community) + {:ok, user} = db_insert(:user) + {:ok, job} = CMS.create_content(community, :job, mock_attrs(:job), user) + # noise + {:ok, job2} = CMS.create_content(community, :job, mock_attrs(:job), user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user) + + {:ok, ~m(user_conn guest_conn community job job2)a} + end + + @query """ + query($id: ID!) { + job(id: $id) { + views + } + } + """ + test "guest user views should +1 after query the job", ~m(guest_conn job)a do + variables = %{id: job.id} + views_1 = guest_conn |> query_result(@query, variables, "job") |> Map.get("views") + + variables = %{id: job.id} + views_2 = guest_conn |> query_result(@query, variables, "job") |> Map.get("views") + assert views_2 == views_1 + 1 + end + + test "login views should +1 after query the job", ~m(user_conn job)a do + variables = %{id: job.id} + views_1 = user_conn |> query_result(@query, variables, "job") |> Map.get("views") + + variables = %{id: job.id} + views_2 = user_conn |> query_result(@query, variables, "job") |> Map.get("views") + assert views_2 == views_1 + 1 + end + + test "login views be record only once in job viewers", ~m(job)a do + {:ok, user} = db_insert(:user) + user_conn = simu_conn(:user, user) + + assert {:error, _} = ORM.find_by(CMS.JobViewer, %{job_id: job.id, user_id: user.id}) + + variables = %{id: job.id} + user_conn |> query_result(@query, variables, "job") |> Map.get("views") + assert {:ok, viewer} = ORM.find_by(CMS.JobViewer, %{job_id: job.id, user_id: user.id}) + assert viewer.job_id == job.id + assert viewer.user_id == user.id + + variables = %{id: job.id} + user_conn |> query_result(@query, variables, "job") |> Map.get("views") + assert {:ok, _} = ORM.find_by(CMS.JobViewer, %{job_id: job.id, user_id: user.id}) + assert viewer.job_id == job.id + assert viewer.user_id == user.id + end + + @paged_query """ + query($filter: PagedArticleFilter!) { + pagedJobs(filter: $filter) { + entries { + id + views + viewerHasViewed + } + } + } + """ + + @query """ + query($id: ID!) { + job(id: $id) { + id + views + viewerHasViewed + } + } + """ + test "user get has viewed flag after query/read the job", ~m(user_conn community job)a do + variables = %{filter: %{community: community.raw}} + results = user_conn |> query_result(@paged_query, variables, "pagedJobs") + found = Enum.find(results["entries"], &(&1["id"] == to_string(job.id))) + assert found["viewerHasViewed"] == false + + variables = %{id: job.id} + result = user_conn |> query_result(@query, variables, "job") + assert result["viewerHasViewed"] == true + + # noise: test viewer dataloader + {:ok, user2} = db_insert(:user) + user_conn2 = simu_conn(:user, user2) + variables = %{filter: %{community: community.raw}} + results = user_conn2 |> query_result(@paged_query, variables, "pagedJobs") + found = Enum.find(results["entries"], &(&1["id"] == to_string(job.id))) + assert found["viewerHasViewed"] == false + + variables = %{filter: %{community: community.raw}} + results = user_conn |> query_result(@paged_query, variables, "pagedJobs") + + found = Enum.find(results["entries"], &(&1["id"] == to_string(job.id))) + assert found["viewerHasViewed"] == true + end +end diff --git a/test/mastani_server_web/query/cms/jobs_flags_test.exs b/test/mastani_server_web/query/cms/jobs_flags_test.exs index a435a3872..e8be98223 100644 --- a/test/mastani_server_web/query/cms/jobs_flags_test.exs +++ b/test/mastani_server_web/query/cms/jobs_flags_test.exs @@ -62,12 +62,12 @@ defmodule MastaniServer.Test.Query.JobsFlags do assert results["pageSize"] == @page_size assert results["totalCount"] == @total_count - CMS.set_community_flags(%Job{id: job_m.id}, community.id, %{pin: true}) + {:ok, _pined_post} = CMS.pin_content(job_m, community) results = guest_conn |> query_result(@query, variables, "pagedJobs") entries_first = results["entries"] |> List.first() - assert results["totalCount"] == @total_count + assert results["totalCount"] == @total_count + 1 assert entries_first["id"] == to_string(job_m.id) assert entries_first["pin"] == true end @@ -78,7 +78,8 @@ defmodule MastaniServer.Test.Query.JobsFlags do assert results |> is_valid_pagination? random_id = results["entries"] |> Enum.shuffle() |> List.first() |> Map.get("id") - {:ok, _} = CMS.set_community_flags(%Job{id: random_id}, community.id, %{pin: true}) + {:ok, _pined_post} = CMS.pin_content(%Job{id: random_id}, community) + # {:ok, _} = CMS.set_community_flags(%Job{id: random_id}, community.id, %{pin: true}) results = guest_conn |> query_result(@query, variables, "pagedJobs") assert results["entries"] |> Enum.any?(&(&1["id"] !== random_id)) diff --git a/test/mastani_server_web/query/cms/post_comment_test.exs b/test/mastani_server_web/query/cms/post_comment_test.exs index 0f8b3273c..2f53dc13a 100644 --- a/test/mastani_server_web/query/cms/post_comment_test.exs +++ b/test/mastani_server_web/query/cms/post_comment_test.exs @@ -13,12 +13,156 @@ defmodule MastaniServer.Test.Query.PostComment do {:ok, ~m(user_conn guest_conn post user)a} end + describe "[post dataloader comment]" do + @query """ + query($filter: PagedArticleFilter) { + pagedPosts(filter: $filter) { + entries { + id + title + commentsParticipatorsCount + commentsParticipators(filter: { first: 5 }) { + id + nickname + } + pagedCommentsParticipators { + entries { + id + } + totalCount + } + commentsCount + } + totalCount + } + } + """ + test "can get comments participators of a post", ~m(user guest_conn)a do + {:ok, user2} = db_insert(:user) + + {:ok, community} = db_insert(:community) + {:ok, post} = CMS.create_content(community, :post, mock_attrs(:post), user) + + variables = %{filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedPosts") + + comments_participators_count = + results["entries"] |> List.first() |> Map.get("commentsParticipatorsCount") + + assert comments_participators_count == 0 + + body = "this is a test comment" + assert {:ok, _comment} = CMS.create_comment(:post, post.id, body, user) + assert {:ok, _comment} = CMS.create_comment(:post, post.id, body, user) + + assert {:ok, _comment} = CMS.create_comment(:post, post.id, body, user2) + + variables = %{filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedPosts") + + comments_participators_count = + results["entries"] |> List.first() |> Map.get("commentsParticipatorsCount") + + comments_count = results["entries"] |> List.first() |> Map.get("commentsCount") + + assert comments_participators_count == 2 + assert comments_count == 3 + + comments_participators = + results["entries"] |> List.first() |> Map.get("commentsParticipators") + + assert comments_participators |> Enum.any?(&(&1["id"] == to_string(user.id))) + assert comments_participators |> Enum.any?(&(&1["id"] == to_string(user2.id))) + end + + test "can get comments participators of a post with multi user", ~m(user guest_conn)a do + body = "this is a test comment" + {:ok, community} = db_insert(:community) + {:ok, post1} = CMS.create_content(community, :post, mock_attrs(:post), user) + {:ok, post2} = CMS.create_content(community, :post, mock_attrs(:post), user) + + {:ok, users_list} = db_insert_multi(:user, 10) + {:ok, users_list2} = db_insert_multi(:user, 10) + + Enum.each( + users_list, + &CMS.create_comment(:post, post1.id, body, &1) + ) + + Enum.each( + users_list2, + &CMS.create_comment(:post, post2.id, body, &1) + ) + + variables = %{filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedPosts") + + assert results["entries"] |> List.first() |> Map.get("commentsParticipators") |> length == 5 + assert results["entries"] |> List.first() |> Map.get("commentsParticipatorsCount") == 10 + + assert results["entries"] |> List.last() |> Map.get("commentsParticipators") |> length == 5 + assert results["entries"] |> List.last() |> Map.get("commentsParticipatorsCount") == 10 + end + + test "can get paged commetns participators of a post", ~m(user guest_conn)a do + body = "this is a test comment" + + {:ok, community} = db_insert(:community) + {:ok, post} = CMS.create_content(community, :post, mock_attrs(:post), user) + {:ok, users_list} = db_insert_multi(:user, 10) + + Enum.each( + users_list, + &CMS.create_comment(:post, post.id, body, &1) + ) + + variables = %{filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedPosts") + participators = results["entries"] |> List.first() |> Map.get("pagedCommentsParticipators") + + assert participators["totalCount"] == 10 + end + end + + @query """ + query($id: ID!, $thread: CmsThread, $filter: PagedFilter!) { + pagedCommentsParticipators(id: $id, thread: $thread, filter: $filter) { + entries { + id + nickname + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + test "can get post's paged commetns participators", ~m(user guest_conn)a do + body = "this is a test comment" + + {:ok, community} = db_insert(:community) + {:ok, post} = CMS.create_content(community, :post, mock_attrs(:post), user) + {:ok, users_list} = db_insert_multi(:user, 10) + + Enum.each( + users_list, + &CMS.create_comment(:post, post.id, body, &1) + ) + + variables = %{id: post.id, thread: "POST", filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "pagedCommentsParticipators") + assert results |> is_valid_pagination?() + + assert results["totalCount"] == 10 + end + # TODO: user can get specific user's replies :list_replies # TODO: filter comment by time / like / reply describe "[post comment]" do @query """ query($id: ID!, $filter: CommentsFilter!) { - comments(id: $id, filter: $filter) { + pagedComments(id: $id, filter: $filter) { entries { id likesCount @@ -41,7 +185,7 @@ defmodule MastaniServer.Test.Query.PostComment do end) variables = %{id: post.id, filter: %{page: 1, size: 10}} - results = guest_conn |> query_result(@query, variables, "comments") + results = guest_conn |> query_result(@query, variables, "pagedComments") assert results |> is_valid_pagination? assert results["totalCount"] == 30 @@ -73,7 +217,7 @@ defmodule MastaniServer.Test.Query.PostComment do {: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, "comments") + results = guest_conn |> query_result(@query, variables, "pagedComments") entries = results["entries"] assert entries |> Enum.at(0) |> Map.get("id") == to_string(comment_3.id) @@ -109,7 +253,7 @@ defmodule MastaniServer.Test.Query.PostComment do {:ok, _} = CMS.dislike_comment(:post_comment, comment_1.id, user_4) variables = %{id: post.id, filter: %{page: 1, size: 10, sort: "MOST_DISLIKES"}} - results = guest_conn |> query_result(@query, variables, "comments") + results = guest_conn |> query_result(@query, variables, "pagedComments") entries = results["entries"] assert entries |> Enum.at(0) |> Map.get("id") == to_string(comment_3.id) @@ -121,7 +265,7 @@ defmodule MastaniServer.Test.Query.PostComment do @query """ query($id: ID!, $filter: CommentsFilter!) { - comments(id: $id, filter: $filter) { + pagedComments(id: $id, filter: $filter) { entries { id viewerHasLiked @@ -137,7 +281,7 @@ defmodule MastaniServer.Test.Query.PostComment do {: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, "comments") + results = user_conn |> query_result(@query, variables, "pagedComments") found = results["entries"] |> Enum.filter(&(&1["id"] == to_string(comment.id))) |> List.first() @@ -145,7 +289,7 @@ defmodule MastaniServer.Test.Query.PostComment do assert found["viewerHasLiked"] == false own_like_conn = simu_conn(:user, user) - results = own_like_conn |> query_result(@query, variables, "comments") + results = own_like_conn |> query_result(@query, variables, "pagedComments") found = results["entries"] |> Enum.filter(&(&1["id"] == to_string(comment.id))) |> List.first() @@ -155,7 +299,7 @@ defmodule MastaniServer.Test.Query.PostComment do @query """ query($id: ID!, $filter: PagedFilter!) { - comments(id: $id, filter: $filter) { + pagedComments(id: $id, filter: $filter) { entries { id body @@ -180,7 +324,7 @@ defmodule MastaniServer.Test.Query.PostComment do {: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, "comments") + results = guest_conn |> query_result(@query, variables, "pagedComments") found = results["entries"] |> Enum.filter(&(&1["id"] == to_string(comment.id))) |> List.first() @@ -195,7 +339,7 @@ defmodule MastaniServer.Test.Query.PostComment do @query """ query($id: ID!, $filter: PagedFilter!) { - comments(id: $id, filter: $filter) { + pagedComments(id: $id, filter: $filter) { entries { id body @@ -220,7 +364,7 @@ defmodule MastaniServer.Test.Query.PostComment do {:ok, _like} = CMS.dislike_comment(:post_comment, comment.id, user) variables = %{id: post.id, filter: %{page: 1, size: 10}} - results = guest_conn |> query_result(@query, variables, "comments") + results = guest_conn |> query_result(@query, variables, "pagedComments") found = results["entries"] |> Enum.filter(&(&1["id"] == to_string(comment.id))) |> List.first() @@ -235,7 +379,7 @@ defmodule MastaniServer.Test.Query.PostComment do @query """ query($id: ID!, $filter: PagedFilter!) { - comments(id: $id, filter: $filter) { + pagedComments(id: $id, filter: $filter) { entries { id body @@ -260,7 +404,7 @@ defmodule MastaniServer.Test.Query.PostComment do {:ok, reply} = CMS.reply_comment(:post, comment.id, "reply body", user) variables = %{id: post.id, filter: %{page: 1, size: 10}} - results = guest_conn |> query_result(@query, variables, "comments") + results = guest_conn |> query_result(@query, variables, "pagedComments") found = results["entries"] |> Enum.filter(&(&1["id"] == to_string(comment.id))) |> List.first() diff --git a/test/mastani_server_web/query/cms/post_test.exs b/test/mastani_server_web/query/cms/post_test.exs index b3f5cbb4c..2943212c6 100644 --- a/test/mastani_server_web/query/cms/post_test.exs +++ b/test/mastani_server_web/query/cms/post_test.exs @@ -57,22 +57,6 @@ defmodule MastaniServer.Test.Query.Post do assert is_valid_kv?(results, "favoritedUsers", :list) end - @query """ - query($id: ID!) { - post(id: $id) { - views - } - } - """ - test "views should +1 after query the post", ~m(user_conn post)a do - variables = %{id: post.id} - views_1 = user_conn |> query_result(@query, variables, "post") |> Map.get("views") - - variables = %{id: post.id} - views_2 = user_conn |> query_result(@query, variables, "post") |> Map.get("views") - assert views_2 == views_1 + 1 - end - @query """ query($id: ID!) { post(id: $id) { @@ -119,4 +103,43 @@ defmodule MastaniServer.Test.Query.Post do variables = %{id: post.id} assert guest_conn |> query_get_error?(@query, variables, ecode(:account_login)) end + + alias MastaniServer.Accounts + + @query """ + query($id: ID!) { + post(id: $id) { + id + favoritedCategoryId + pagedCommentsParticipators { + entries { + id + } + totalCount + } + } + } + """ + 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/mastani_server_web/query/cms/post_viewer_test.exs b/test/mastani_server_web/query/cms/post_viewer_test.exs new file mode 100644 index 000000000..ef3516f41 --- /dev/null +++ b/test/mastani_server_web/query/cms/post_viewer_test.exs @@ -0,0 +1,109 @@ +defmodule MastaniServer.Test.Query.PostViewer do + use MastaniServer.TestTools + + alias Helper.ORM + alias MastaniServer.CMS + + setup do + {:ok, community} = db_insert(:community) + {:ok, user} = db_insert(:user) + {:ok, post} = CMS.create_content(community, :post, mock_attrs(:post), user) + # noise + {:ok, post2} = CMS.create_content(community, :post, mock_attrs(:post), user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user) + + {:ok, ~m(user_conn guest_conn community post post2)a} + end + + @query """ + query($id: ID!) { + post(id: $id) { + views + } + } + """ + test "guest user views should +1 after query the post", ~m(guest_conn post)a do + variables = %{id: post.id} + views_1 = guest_conn |> query_result(@query, variables, "post") |> Map.get("views") + + variables = %{id: post.id} + views_2 = guest_conn |> query_result(@query, variables, "post") |> Map.get("views") + assert views_2 == views_1 + 1 + end + + test "login views should +1 after query the post", ~m(user_conn post)a do + variables = %{id: post.id} + views_1 = user_conn |> query_result(@query, variables, "post") |> Map.get("views") + + variables = %{id: post.id} + views_2 = user_conn |> query_result(@query, variables, "post") |> Map.get("views") + assert views_2 == views_1 + 1 + end + + test "login views be record only once in post viewers", ~m(post)a do + {:ok, user} = db_insert(:user) + user_conn = simu_conn(:user, user) + + assert {:error, _} = ORM.find_by(CMS.PostViewer, %{post_id: post.id, user_id: user.id}) + + variables = %{id: post.id} + user_conn |> query_result(@query, variables, "post") |> Map.get("views") + assert {:ok, viewer} = ORM.find_by(CMS.PostViewer, %{post_id: post.id, user_id: user.id}) + assert viewer.post_id == post.id + assert viewer.user_id == user.id + + variables = %{id: post.id} + user_conn |> query_result(@query, variables, "post") |> Map.get("views") + assert {:ok, _} = ORM.find_by(CMS.PostViewer, %{post_id: post.id, user_id: user.id}) + assert viewer.post_id == post.id + assert viewer.user_id == user.id + end + + @paged_query """ + query($filter: PagedArticleFilter!) { + pagedPosts(filter: $filter) { + entries { + id + views + viewerHasViewed + } + } + } + """ + + @query """ + query($id: ID!) { + post(id: $id) { + id + views + viewerHasViewed + } + } + """ + test "user get has viewed flag after query/read the post", ~m(user_conn community post)a do + variables = %{filter: %{community: community.raw}} + results = user_conn |> query_result(@paged_query, variables, "pagedPosts") + found = Enum.find(results["entries"], &(&1["id"] == to_string(post.id))) + assert found["viewerHasViewed"] == false + + variables = %{id: post.id} + result = user_conn |> query_result(@query, variables, "post") + assert result["viewerHasViewed"] == true + + # noise: test viewer dataloader + {:ok, user2} = db_insert(:user) + user_conn2 = simu_conn(:user, user2) + variables = %{filter: %{community: community.raw}} + results = user_conn2 |> query_result(@paged_query, variables, "pagedPosts") + found = Enum.find(results["entries"], &(&1["id"] == to_string(post.id))) + assert found["viewerHasViewed"] == false + + variables = %{filter: %{community: community.raw}} + results = user_conn |> query_result(@paged_query, variables, "pagedPosts") + + found = Enum.find(results["entries"], &(&1["id"] == to_string(post.id))) + assert found["viewerHasViewed"] == true + end +end diff --git a/test/mastani_server_web/query/cms/posts_flags_test.exs b/test/mastani_server_web/query/cms/posts_flags_test.exs index b470b2df8..dd0828504 100644 --- a/test/mastani_server_web/query/cms/posts_flags_test.exs +++ b/test/mastani_server_web/query/cms/posts_flags_test.exs @@ -62,12 +62,12 @@ defmodule MastaniServer.Test.Query.PostsFlags do assert results["pageSize"] == @page_size assert results["totalCount"] == @total_count - CMS.set_community_flags(%Post{id: post_m.id}, community.id, %{pin: true}) + {:ok, _pined_post} = CMS.pin_content(post_m, community, "posts") results = guest_conn |> query_result(@query, variables, "pagedPosts") entries_first = results["entries"] |> List.first() - assert results["totalCount"] == @total_count + assert results["totalCount"] == @total_count + 1 assert entries_first["id"] == to_string(post_m.id) assert entries_first["pin"] == true end @@ -78,7 +78,10 @@ defmodule MastaniServer.Test.Query.PostsFlags do assert results |> is_valid_pagination? random_id = results["entries"] |> Enum.shuffle() |> List.first() |> Map.get("id") - {:ok, _} = CMS.set_community_flags(%Post{id: random_id}, community.id, %{pin: true}) + + {:ok, _pined_post} = CMS.pin_content(%Post{id: random_id}, community, "posts") + + # {:ok, _} = CMS.set_community_flags(%Post{id: random_id}, community.id, %{pin: true}) results = guest_conn |> query_result(@query, variables, "pagedPosts") assert results["entries"] |> Enum.any?(&(&1["id"] !== random_id)) diff --git a/test/mastani_server_web/query/cms/posts_topic_test.exs b/test/mastani_server_web/query/cms/posts_topic_test.exs new file mode 100644 index 000000000..09597adb7 --- /dev/null +++ b/test/mastani_server_web/query/cms/posts_topic_test.exs @@ -0,0 +1,125 @@ +defmodule MastaniServer.Test.Query.PostsTopic do + use MastaniServer.TestTools + + alias MastaniServer.CMS + + setup do + {:ok, user} = db_insert(:user) + {:ok, community} = db_insert(:community) + post_attrs = mock_attrs(:post, %{community_id: community.id, topic: "posts"}) + {:ok, _post} = CMS.create_content(community, :post, post_attrs, user) + post_attrs = mock_attrs(:post, %{community_id: community.id, topic: "posts"}) + {:ok, _post} = CMS.create_content(community, :post, post_attrs, user) + + guest_conn = simu_conn(:guest) + + {:ok, ~m(guest_conn user community)a} + end + + describe "[query posts topic filter]" do + @create_post_query """ + mutation( + $title: String! + $body: String! + $digest: String! + $length: Int! + $communityId: ID! + $tags: [Ids] + $topic: CmsTopic + ) { + createPost( + title: $title + body: $body + digest: $digest + length: $length + communityId: $communityId + tags: $tags + topic: $topic + ) { + title + body + id + } + } + """ + @query """ + query($filter: PagedPostsFilter!) { + pagedPosts(filter: $filter) { + entries { + id + title + } + totalCount + } + } + """ + test "create post with valid args and topic ", ~m(guest_conn)a do + {:ok, user} = db_insert(:user) + user_conn = simu_conn(:user, user) + + {:ok, community} = db_insert(:community) + post_attr = mock_attrs(:post) + + variables = post_attr |> Map.merge(%{communityId: community.id, topic: "city"}) + _created = user_conn |> mutation_result(@create_post_query, variables, "createPost") + + variables = %{filter: %{page: 1, size: 10, topic: "city"}} + results = guest_conn |> query_result(@query, variables, "pagedPosts") + + assert results["totalCount"] == 1 + end + + @query """ + query($filter: PagedPostsFilter!) { + pagedPosts(filter: $filter) { + entries { + id + title + } + totalCount + } + } + """ + test "topic filter on posts should work", ~m(guest_conn)a do + variables = %{filter: %{page: 1, size: 10}} + results = guest_conn |> query_result(@query, variables, "pagedPosts") + assert results["totalCount"] == 2 + + variables = %{filter: %{page: 1, size: 10, topic: "posts"}} + results = guest_conn |> query_result(@query, variables, "pagedPosts") + assert results["totalCount"] == 2 + + variables = %{filter: %{page: 1, size: 10, topic: "other"}} + results = guest_conn |> query_result(@query, variables, "pagedPosts") + assert results["totalCount"] == 0 + end + end + + describe "[query non-posts topic filter]" do + @query """ + query($filter: PagedArticleFilter!) { + pagedJobs(filter: $filter) { + entries { + id + title + } + totalCount + } + } + """ + test "topic filter on non-posts has no effect", ~m(guest_conn user community)a do + job_attrs = mock_attrs(:job, %{community_id: community.id, topic: "posts"}) + {:ok, _} = CMS.create_content(community, :job, job_attrs, user) + + job_attrs = mock_attrs(:job, %{community_id: community.id, topic: "city"}) + {:ok, _} = CMS.create_content(community, :job, job_attrs, user) + + variables = %{filter: %{community: community.raw, page: 1, size: 10}} + results = guest_conn |> query_result(@query, variables, "pagedJobs") + assert results["totalCount"] == 2 + + variables = %{filter: %{page: 1, size: 10, topic: "posts"}} + assert guest_conn |> query_get_error?(@query, variables) + end + end +end diff --git a/test/mastani_server_web/query/cms/reaction_users_test.exs b/test/mastani_server_web/query/cms/reaction_users_test.exs new file mode 100644 index 000000000..1ddf59d23 --- /dev/null +++ b/test/mastani_server_web/query/cms/reaction_users_test.exs @@ -0,0 +1,117 @@ +defmodule MastaniServer.Test.Query.ReactionUsers do + use MastaniServer.TestTools + + alias MastaniServer.CMS + + setup do + {:ok, post} = db_insert(:post) + {:ok, job} = db_insert(:job) + {:ok, video} = db_insert(:video) + {: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 video)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 + + test "guest can get favroted user list after favrote to a video", + ~m(guest_conn video user user2)a do + {:ok, _} = CMS.reaction(:video, :favorite, video.id, user) + {:ok, _} = CMS.reaction(:video, :favorite, video.id, user2) + + variables = %{ + id: video.id, + thread: "VIDEO", + 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 + + test "guest can get stared user list after star to a video", + ~m(guest_conn video user user2)a do + {:ok, _} = CMS.reaction(:video, :star, video.id, user) + {:ok, _} = CMS.reaction(:video, :star, video.id, user2) + + variables = %{id: video.id, thread: "VIDEO", 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/mastani_server_web/query/cms/repo_comment_test.exs b/test/mastani_server_web/query/cms/repo_comment_test.exs new file mode 100644 index 000000000..c0ed76112 --- /dev/null +++ b/test/mastani_server_web/query/cms/repo_comment_test.exs @@ -0,0 +1,358 @@ +defmodule MastaniServer.Test.Query.RepoComment do + use MastaniServer.TestTools + + alias MastaniServer.CMS + + setup do + {:ok, repo} = db_insert(:repo) + {:ok, user} = db_insert(:user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user) + + {:ok, ~m(user_conn guest_conn repo user)a} + end + + describe "[repo dataloader comment]" do + @query """ + query($filter: PagedArticleFilter) { + pagedRepos(filter: $filter) { + entries { + id + title + commentsParticipators(filter: { first: 5 }) { + id + nickname + } + pagedCommentsParticipators { + entries { + id + } + totalCount + } + commentsCount + } + totalCount + } + } + """ + test "can get comments participators of a repo", ~m(user guest_conn)a do + {:ok, user2} = db_insert(:user) + + {:ok, community} = db_insert(:community) + {:ok, repo} = CMS.create_content(community, :repo, mock_attrs(:repo), user) + + variables = %{thread: "REPO", filter: %{community: community.raw}} + guest_conn |> query_result(@query, variables, "pagedRepos") + + body = "this is a test comment" + assert {:ok, _comment} = CMS.create_comment(:repo, repo.id, body, user) + assert {:ok, _comment} = CMS.create_comment(:repo, repo.id, body, user) + + assert {:ok, _comment} = CMS.create_comment(:repo, repo.id, body, user2) + + variables = %{filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedRepos") + + comments_count = results["entries"] |> List.first() |> Map.get("commentsCount") + + assert comments_count == 3 + end + + test "can get comments participators of a repo with multi user", ~m(user guest_conn)a do + body = "this is a test comment" + {:ok, community} = db_insert(:community) + {:ok, repo1} = CMS.create_content(community, :repo, mock_attrs(:repo), user) + {:ok, repo2} = CMS.create_content(community, :repo, mock_attrs(:repo), user) + + {:ok, users_list} = db_insert_multi(:user, 10) + {:ok, users_list2} = db_insert_multi(:user, 10) + + Enum.each( + users_list, + &CMS.create_comment(:repo, repo1.id, body, &1) + ) + + Enum.each( + users_list2, + &CMS.create_comment(:repo, repo2.id, body, &1) + ) + + variables = %{thread: "REPO", filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedRepos") + + assert results["entries"] |> List.first() |> Map.get("commentsParticipators") |> length == 5 + assert results["entries"] |> List.last() |> Map.get("commentsParticipators") |> length == 5 + end + + test "can get paged commetns participators of a repo", ~m(user guest_conn)a do + body = "this is a test comment" + + {:ok, community} = db_insert(:community) + {:ok, repo} = CMS.create_content(community, :repo, mock_attrs(:repo), user) + {:ok, users_list} = db_insert_multi(:user, 10) + + Enum.each( + users_list, + &CMS.create_comment(:repo, repo.id, body, &1) + ) + + variables = %{filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedRepos") + participators = results["entries"] |> List.first() |> Map.get("pagedCommentsParticipators") + + assert participators["totalCount"] == 10 + end + end + + @query """ + query($id: ID!, $thread: CmsThread, $filter: PagedFilter!) { + pagedCommentsParticipators(id: $id, thread: $thread, filter: $filter) { + entries { + id + nickname + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + test "can get repo's paged commetns participators", ~m(user guest_conn)a do + body = "this is a test comment" + + {:ok, community} = db_insert(:community) + {:ok, repo} = CMS.create_content(community, :repo, mock_attrs(:repo), user) + {:ok, users_list} = db_insert_multi(:user, 10) + + Enum.each( + users_list, + &CMS.create_comment(:repo, repo.id, body, &1) + ) + + variables = %{id: repo.id, thread: "REPO", filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "pagedCommentsParticipators") + assert results |> is_valid_pagination?() + + assert results["totalCount"] == 10 + end + + # TODO: user can get specific user's replies :list_replies + describe "[repo comment]" do + @query """ + query($filter: PagedArticleFilter) { + pagedRepos(filter: $filter) { + entries { + id + title + commentsCount + } + totalCount + } + } + """ + test "can get comments info in paged repos", ~m(user guest_conn)a do + body = "this is a test comment" + + {:ok, community} = db_insert(:community) + {:ok, repo} = CMS.create_content(community, :repo, mock_attrs(:repo), user) + {:ok, _comment} = CMS.create_comment(:repo, repo.id, body, user) + + variables = %{filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedRepos") + + assert results["entries"] |> List.first() |> Map.get("commentsCount") == 1 + end + + @query """ + query($thread: CmsThread, $id: ID!, $filter: CommentsFilter!) { + pagedComments(thread: $thread, id: $id, filter: $filter) { + entries { + id + body + likesCount + dislikesCount + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + test "guest user can get a paged comment", ~m(guest_conn repo user)a do + body = "test comment" + + Enum.reduce(1..30, [], fn _, acc -> + {:ok, value} = CMS.create_comment(:repo, repo.id, body, user) + + acc ++ [value] + end) + + variables = %{thread: "REPO", id: repo.id, filter: %{page: 1, size: 10}} + results = guest_conn |> query_result(@query, variables, "pagedComments") + + assert results |> is_valid_pagination? + assert results["totalCount"] == 30 + end + + test "MOST_LIKES filter should work", ~m(guest_conn repo user)a do + body = "test comment" + + comments = + Enum.reduce(1..10, [], fn _, acc -> + {:ok, value} = CMS.create_comment(:repo, repo.id, 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(:repo_comment, comment_3.id, user_1) + {:ok, _} = CMS.like_comment(:repo_comment, comment_3.id, user_2) + {:ok, _} = CMS.like_comment(:repo_comment, comment_3.id, user_3) + {:ok, _} = CMS.like_comment(:repo_comment, comment_3.id, user_4) + {:ok, _} = CMS.like_comment(:repo_comment, comment_3.id, user_5) + + {:ok, _} = CMS.like_comment(:repo_comment, comment_1.id, user_1) + {:ok, _} = CMS.like_comment(:repo_comment, comment_1.id, user_2) + {:ok, _} = CMS.like_comment(:repo_comment, comment_1.id, user_3) + {:ok, _} = CMS.like_comment(:repo_comment, comment_1.id, user_4) + + variables = %{ + thread: "REPO", + id: repo.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 + + test "MOST_DISLIKES filter should work", ~m(guest_conn repo user)a do + body = "test comment" + + comments = + Enum.reduce(1..10, [], fn _, acc -> + {:ok, value} = CMS.create_comment(:repo, repo.id, 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.dislike_comment(:repo_comment, comment_3.id, user_1) + {:ok, _} = CMS.dislike_comment(:repo_comment, comment_3.id, user_2) + {:ok, _} = CMS.dislike_comment(:repo_comment, comment_3.id, user_3) + {:ok, _} = CMS.dislike_comment(:repo_comment, comment_3.id, user_4) + {:ok, _} = CMS.dislike_comment(:repo_comment, comment_3.id, user_5) + + {:ok, _} = CMS.dislike_comment(:repo_comment, comment_1.id, user_1) + {:ok, _} = CMS.dislike_comment(:repo_comment, comment_1.id, user_2) + {:ok, _} = CMS.dislike_comment(:repo_comment, comment_1.id, user_3) + {:ok, _} = CMS.dislike_comment(:repo_comment, comment_1.id, user_4) + + variables = %{ + thread: "REPO", + id: repo.id, + filter: %{page: 1, size: 10, sort: "MOST_DISLIKES"} + } + + 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("dislikesCount") == 5 + + assert entries |> Enum.at(1) |> Map.get("id") == to_string(comment_1.id) + assert entries |> Enum.at(1) |> Map.get("dislikesCount") == 4 + end + + @query """ + query($thread: CmsThread, $id: ID!, $filter: CommentsFilter!) { + pagedComments(thread: $thread, id: $id, filter: $filter) { + entries { + id + viewerHasLiked + } + } + } + """ + test "login user can get hasLiked feedBack", ~m(user_conn repo user)a do + body = "test comment" + + {:ok, comment} = CMS.create_comment(:repo, repo.id, body, user) + + {:ok, _like} = CMS.like_comment(:repo_comment, comment.id, user) + + variables = %{thread: "REPO", id: repo.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($thread: CmsThread, $id: ID!, $filter: PagedFilter!) { + pagedComments(thread: $thread, id: $id, filter: $filter) { + entries { + id + body + replyTo { + id + body + } + repliesCount + replies { + id + body + } + } + } + } + """ + test "guest user can get replies info", ~m(guest_conn repo user)a do + body = "test comment" + {:ok, comment} = CMS.create_comment(:repo, repo.id, body, user) + + {:ok, reply} = CMS.reply_comment(:repo, comment.id, "reply body", user) + + variables = %{thread: "REPO", id: repo.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() + + found_reply = + results["entries"] |> Enum.filter(&(&1["id"] == to_string(reply.id))) |> List.first() + + assert found["repliesCount"] == 1 + assert found["replies"] |> Enum.any?(&(&1["id"] == to_string(reply.id))) + assert found["replyTo"] == nil + assert found_reply["replyTo"] |> Map.get("id") == to_string(comment.id) + end + end +end diff --git a/test/mastani_server_web/query/cms/repo_test.exs b/test/mastani_server_web/query/cms/repo_test.exs index 0de2ce92b..c7905020a 100644 --- a/test/mastani_server_web/query/cms/repo_test.exs +++ b/test/mastani_server_web/query/cms/repo_test.exs @@ -14,32 +14,103 @@ defmodule MastaniServer.Test.Query.Repo do query($id: ID!) { repo(id: $id) { id - repo_name + title + readme } } """ - test "basic graphql query on repo by user", ~m(guest_conn repo)a do + test "basic graphql query on repo with logined user", ~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, "title", :string) + assert is_valid_kv?(results, "readme", :string) + assert length(Map.keys(results)) == 3 + end + + test "basic graphql query on repo with stranger(unloged user)", ~m(guest_conn repo)a do variables = %{id: repo.id} results = guest_conn |> query_result(@query, variables, "repo") assert results["id"] == to_string(repo.id) - assert is_valid_kv?(results, "repo_name", :string) - assert length(Map.keys(results)) == 2 + 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) { - views + id + title + viewerHasFavorited + favoritedCount } } """ - test "views should +1 after query the repo", ~m(user_conn repo)a do + test "logged user can query viewerHasFavorited field", ~m(user_conn repo)a do variables = %{id: repo.id} - views_1 = user_conn |> query_result(@query, variables, "repo") |> Map.get("views") + 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} - views_2 = user_conn |> query_result(@query, variables, "repo") |> Map.get("views") - assert views_2 == views_1 + 1 + + assert guest_conn |> query_get_error?(@query, variables) + end + + alias MastaniServer.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/mastani_server_web/query/cms/repo_viewer_test.exs b/test/mastani_server_web/query/cms/repo_viewer_test.exs new file mode 100644 index 000000000..48e184927 --- /dev/null +++ b/test/mastani_server_web/query/cms/repo_viewer_test.exs @@ -0,0 +1,109 @@ +defmodule MastaniServer.Test.Query.RepoViewer do + use MastaniServer.TestTools + + alias Helper.ORM + alias MastaniServer.CMS + + setup do + {:ok, community} = db_insert(:community) + {:ok, user} = db_insert(:user) + {:ok, repo} = CMS.create_content(community, :repo, mock_attrs(:repo), user) + # noise + {:ok, repo2} = CMS.create_content(community, :repo, mock_attrs(:repo), user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user) + + {:ok, ~m(user_conn guest_conn community repo repo2)a} + end + + @query """ + query($id: ID!) { + repo(id: $id) { + views + } + } + """ + test "guest user views should +1 after query the repo", ~m(guest_conn repo)a do + variables = %{id: repo.id} + views_1 = guest_conn |> query_result(@query, variables, "repo") |> Map.get("views") + + variables = %{id: repo.id} + views_2 = guest_conn |> query_result(@query, variables, "repo") |> Map.get("views") + assert views_2 == views_1 + 1 + end + + test "login views should +1 after query the repo", ~m(user_conn repo)a do + variables = %{id: repo.id} + views_1 = user_conn |> query_result(@query, variables, "repo") |> Map.get("views") + + variables = %{id: repo.id} + views_2 = user_conn |> query_result(@query, variables, "repo") |> Map.get("views") + assert views_2 == views_1 + 1 + end + + test "login views be record only once in repo viewers", ~m(repo)a do + {:ok, user} = db_insert(:user) + user_conn = simu_conn(:user, user) + + assert {:error, _} = ORM.find_by(CMS.RepoViewer, %{repo_id: repo.id, user_id: user.id}) + + variables = %{id: repo.id} + user_conn |> query_result(@query, variables, "repo") |> Map.get("views") + assert {:ok, viewer} = ORM.find_by(CMS.RepoViewer, %{repo_id: repo.id, user_id: user.id}) + assert viewer.repo_id == repo.id + assert viewer.user_id == user.id + + variables = %{id: repo.id} + user_conn |> query_result(@query, variables, "repo") |> Map.get("views") + assert {:ok, _} = ORM.find_by(CMS.RepoViewer, %{repo_id: repo.id, user_id: user.id}) + assert viewer.repo_id == repo.id + assert viewer.user_id == user.id + end + + @paged_query """ + query($filter: PagedArticleFilter!) { + pagedRepos(filter: $filter) { + entries { + id + views + viewerHasViewed + } + } + } + """ + + @query """ + query($id: ID!) { + repo(id: $id) { + id + views + viewerHasViewed + } + } + """ + test "user get has viewed flag after query/read the repo", ~m(user_conn community repo)a do + variables = %{filter: %{community: community.raw}} + results = user_conn |> query_result(@paged_query, variables, "pagedRepos") + found = Enum.find(results["entries"], &(&1["id"] == to_string(repo.id))) + assert found["viewerHasViewed"] == false + + variables = %{id: repo.id} + result = user_conn |> query_result(@query, variables, "repo") + assert result["viewerHasViewed"] == true + + # noise: test viewer dataloader + {:ok, user2} = db_insert(:user) + user_conn2 = simu_conn(:user, user2) + variables = %{filter: %{community: community.raw}} + results = user_conn2 |> query_result(@paged_query, variables, "pagedRepos") + found = Enum.find(results["entries"], &(&1["id"] == to_string(repo.id))) + assert found["viewerHasViewed"] == false + + variables = %{filter: %{community: community.raw}} + results = user_conn |> query_result(@paged_query, variables, "pagedRepos") + + found = Enum.find(results["entries"], &(&1["id"] == to_string(repo.id))) + assert found["viewerHasViewed"] == true + end +end diff --git a/test/mastani_server_web/query/cms/repos_flags_test.exs b/test/mastani_server_web/query/cms/repos_flags_test.exs index 03e31f643..71608d93f 100644 --- a/test/mastani_server_web/query/cms/repos_flags_test.exs +++ b/test/mastani_server_web/query/cms/repos_flags_test.exs @@ -61,12 +61,12 @@ defmodule MastaniServer.Test.Query.ReposFlags do assert results["pageSize"] == @page_size assert results["totalCount"] == @total_count - CMS.set_community_flags(%Repo{id: repo_m.id}, community.id, %{pin: true}) + {:ok, _pined_post} = CMS.pin_content(repo_m, community) results = guest_conn |> query_result(@query, variables, "pagedRepos") entries_first = results["entries"] |> List.first() - assert results["totalCount"] == @total_count + assert results["totalCount"] == @total_count + 1 assert entries_first["id"] == to_string(repo_m.id) assert entries_first["pin"] == true end @@ -77,7 +77,8 @@ defmodule MastaniServer.Test.Query.ReposFlags do assert results |> is_valid_pagination? random_id = results["entries"] |> Enum.shuffle() |> List.first() |> Map.get("id") - {:ok, _} = CMS.set_community_flags(%Repo{id: random_id}, community.id, %{pin: true}) + + {:ok, _pined_post} = CMS.pin_content(%Repo{id: random_id}, community) results = guest_conn |> query_result(@query, variables, "pagedRepos") assert results["entries"] |> Enum.any?(&(&1["id"] !== random_id)) diff --git a/test/mastani_server_web/query/cms/video_comment_test.exs b/test/mastani_server_web/query/cms/video_comment_test.exs new file mode 100644 index 000000000..3ded7bd21 --- /dev/null +++ b/test/mastani_server_web/query/cms/video_comment_test.exs @@ -0,0 +1,358 @@ +defmodule MastaniServer.Test.Query.VideoComment do + use MastaniServer.TestTools + + alias MastaniServer.CMS + + setup do + {:ok, video} = db_insert(:video) + {:ok, user} = db_insert(:user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user) + + {:ok, ~m(user_conn guest_conn video user)a} + end + + describe "[video dataloader comment]" do + @query """ + query($filter: PagedArticleFilter) { + pagedVideos(filter: $filter) { + entries { + id + title + commentsParticipators(filter: { first: 5 }) { + id + nickname + } + pagedCommentsParticipators { + entries { + id + } + totalCount + } + commentsCount + } + totalCount + } + } + """ + test "can get comments participators of a video", ~m(user guest_conn)a do + {:ok, user2} = db_insert(:user) + + {:ok, community} = db_insert(:community) + {:ok, video} = CMS.create_content(community, :video, mock_attrs(:video), user) + + variables = %{thread: "VIDEO", filter: %{community: community.raw}} + guest_conn |> query_result(@query, variables, "pagedVideos") + + body = "this is a test comment" + assert {:ok, _comment} = CMS.create_comment(:video, video.id, body, user) + assert {:ok, _comment} = CMS.create_comment(:video, video.id, body, user) + + assert {:ok, _comment} = CMS.create_comment(:video, video.id, body, user2) + + variables = %{filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedVideos") + + comments_count = results["entries"] |> List.first() |> Map.get("commentsCount") + + assert comments_count == 3 + end + + test "can get comments participators of a video with multi user", ~m(user guest_conn)a do + body = "this is a test comment" + {:ok, community} = db_insert(:community) + {:ok, video1} = CMS.create_content(community, :video, mock_attrs(:video), user) + {:ok, video2} = CMS.create_content(community, :video, mock_attrs(:video), user) + + {:ok, users_list} = db_insert_multi(:user, 10) + {:ok, users_list2} = db_insert_multi(:user, 10) + + Enum.each( + users_list, + &CMS.create_comment(:video, video1.id, body, &1) + ) + + Enum.each( + users_list2, + &CMS.create_comment(:video, video2.id, body, &1) + ) + + variables = %{thread: "VIDEO", filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedVideos") + + assert results["entries"] |> List.first() |> Map.get("commentsParticipators") |> length == 5 + assert results["entries"] |> List.last() |> Map.get("commentsParticipators") |> length == 5 + end + + test "can get paged commetns participators of a video", ~m(user guest_conn)a do + body = "this is a test comment" + + {:ok, community} = db_insert(:community) + {:ok, video} = CMS.create_content(community, :video, mock_attrs(:video), user) + {:ok, users_list} = db_insert_multi(:user, 10) + + Enum.each( + users_list, + &CMS.create_comment(:video, video.id, body, &1) + ) + + variables = %{filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedVideos") + participators = results["entries"] |> List.first() |> Map.get("pagedCommentsParticipators") + + assert participators["totalCount"] == 10 + end + end + + @query """ + query($id: ID!, $thread: CmsThread, $filter: PagedFilter!) { + pagedCommentsParticipators(id: $id, thread: $thread, filter: $filter) { + entries { + id + nickname + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + test "can get video's paged commetns participators", ~m(user guest_conn)a do + body = "this is a test comment" + + {:ok, community} = db_insert(:community) + {:ok, video} = CMS.create_content(community, :video, mock_attrs(:video), user) + {:ok, users_list} = db_insert_multi(:user, 10) + + Enum.each( + users_list, + &CMS.create_comment(:video, video.id, body, &1) + ) + + variables = %{id: video.id, thread: "VIDEO", filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "pagedCommentsParticipators") + assert results |> is_valid_pagination?() + + assert results["totalCount"] == 10 + end + + # TODO: user can get specific user's replies :list_replies + describe "[video comment]" do + @query """ + query($filter: PagedArticleFilter) { + pagedVideos(filter: $filter) { + entries { + id + title + commentsCount + } + totalCount + } + } + """ + test "can get comments info in paged videos", ~m(user guest_conn)a do + body = "this is a test comment" + + {:ok, community} = db_insert(:community) + {:ok, video} = CMS.create_content(community, :video, mock_attrs(:video), user) + {:ok, _comment} = CMS.create_comment(:video, video.id, body, user) + + variables = %{filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedVideos") + + assert results["entries"] |> List.first() |> Map.get("commentsCount") == 1 + end + + @query """ + query($thread: CmsThread, $id: ID!, $filter: CommentsFilter!) { + pagedComments(thread: $thread, id: $id, filter: $filter) { + entries { + id + body + likesCount + dislikesCount + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + test "guest user can get a paged comment", ~m(guest_conn video user)a do + body = "test comment" + + Enum.reduce(1..30, [], fn _, acc -> + {:ok, value} = CMS.create_comment(:video, video.id, body, user) + + acc ++ [value] + end) + + variables = %{thread: "VIDEO", id: video.id, filter: %{page: 1, size: 10}} + results = guest_conn |> query_result(@query, variables, "pagedComments") + + assert results |> is_valid_pagination? + assert results["totalCount"] == 30 + end + + test "MOST_LIKES filter should work", ~m(guest_conn video user)a do + body = "test comment" + + comments = + Enum.reduce(1..10, [], fn _, acc -> + {:ok, value} = CMS.create_comment(:video, video.id, 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(:video_comment, comment_3.id, user_1) + {:ok, _} = CMS.like_comment(:video_comment, comment_3.id, user_2) + {:ok, _} = CMS.like_comment(:video_comment, comment_3.id, user_3) + {:ok, _} = CMS.like_comment(:video_comment, comment_3.id, user_4) + {:ok, _} = CMS.like_comment(:video_comment, comment_3.id, user_5) + + {:ok, _} = CMS.like_comment(:video_comment, comment_1.id, user_1) + {:ok, _} = CMS.like_comment(:video_comment, comment_1.id, user_2) + {:ok, _} = CMS.like_comment(:video_comment, comment_1.id, user_3) + {:ok, _} = CMS.like_comment(:video_comment, comment_1.id, user_4) + + variables = %{ + thread: "VIDEO", + id: video.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 + + test "MOST_DISLIKES filter should work", ~m(guest_conn video user)a do + body = "test comment" + + comments = + Enum.reduce(1..10, [], fn _, acc -> + {:ok, value} = CMS.create_comment(:video, video.id, 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.dislike_comment(:video_comment, comment_3.id, user_1) + {:ok, _} = CMS.dislike_comment(:video_comment, comment_3.id, user_2) + {:ok, _} = CMS.dislike_comment(:video_comment, comment_3.id, user_3) + {:ok, _} = CMS.dislike_comment(:video_comment, comment_3.id, user_4) + {:ok, _} = CMS.dislike_comment(:video_comment, comment_3.id, user_5) + + {:ok, _} = CMS.dislike_comment(:video_comment, comment_1.id, user_1) + {:ok, _} = CMS.dislike_comment(:video_comment, comment_1.id, user_2) + {:ok, _} = CMS.dislike_comment(:video_comment, comment_1.id, user_3) + {:ok, _} = CMS.dislike_comment(:video_comment, comment_1.id, user_4) + + variables = %{ + thread: "VIDEO", + id: video.id, + filter: %{page: 1, size: 10, sort: "MOST_DISLIKES"} + } + + 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("dislikesCount") == 5 + + assert entries |> Enum.at(1) |> Map.get("id") == to_string(comment_1.id) + assert entries |> Enum.at(1) |> Map.get("dislikesCount") == 4 + end + + @query """ + query($thread: CmsThread, $id: ID!, $filter: CommentsFilter!) { + pagedComments(thread: $thread, id: $id, filter: $filter) { + entries { + id + viewerHasLiked + } + } + } + """ + test "login user can get hasLiked feedBack", ~m(user_conn video user)a do + body = "test comment" + + {:ok, comment} = CMS.create_comment(:video, video.id, body, user) + + {:ok, _like} = CMS.like_comment(:video_comment, comment.id, user) + + variables = %{thread: "VIDEO", id: video.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($thread: CmsThread, $id: ID!, $filter: PagedFilter!) { + pagedComments(thread: $thread, id: $id, filter: $filter) { + entries { + id + body + replyTo { + id + body + } + repliesCount + replies { + id + body + } + } + } + } + """ + test "guest user can get replies info", ~m(guest_conn video user)a do + body = "test comment" + {:ok, comment} = CMS.create_comment(:video, video.id, body, user) + + {:ok, reply} = CMS.reply_comment(:video, comment.id, "reply body", user) + + variables = %{thread: "VIDEO", id: video.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() + + found_reply = + results["entries"] |> Enum.filter(&(&1["id"] == to_string(reply.id))) |> List.first() + + assert found["repliesCount"] == 1 + assert found["replies"] |> Enum.any?(&(&1["id"] == to_string(reply.id))) + assert found["replyTo"] == nil + assert found_reply["replyTo"] |> Map.get("id") == to_string(comment.id) + end + end +end diff --git a/test/mastani_server_web/query/cms/video_test.exs b/test/mastani_server_web/query/cms/video_test.exs index 82a38da88..d0de66506 100644 --- a/test/mastani_server_web/query/cms/video_test.exs +++ b/test/mastani_server_web/query/cms/video_test.exs @@ -27,18 +27,36 @@ defmodule MastaniServer.Test.Query.Video do assert length(Map.keys(results)) == 2 end + alias MastaniServer.Accounts + @query """ query($id: ID!) { video(id: $id) { - views + id + favoritedCategoryId } } """ - test "views should +1 after query the video", ~m(user_conn video)a do + test "login user can get nil video favorited category id", ~m(video)a do + {:ok, user} = db_insert(:user) + user_conn = simu_conn(:user, user) + variables = %{id: video.id} - views_1 = user_conn |> query_result(@query, variables, "video") |> Map.get("views") + result = user_conn |> query_result(@query, variables, "video") + assert result["favoritedCategoryId"] == nil + end + + test "login user can get video favorited category id after favorited", ~m(video)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, :video, video.id, category.id) + variables = %{id: video.id} - views_2 = user_conn |> query_result(@query, variables, "video") |> Map.get("views") - assert views_2 == views_1 + 1 + result = user_conn |> query_result(@query, variables, "video") + + assert result["favoritedCategoryId"] == to_string(category.id) end end diff --git a/test/mastani_server_web/query/cms/video_viewer_test.exs b/test/mastani_server_web/query/cms/video_viewer_test.exs new file mode 100644 index 000000000..fdc0e07d7 --- /dev/null +++ b/test/mastani_server_web/query/cms/video_viewer_test.exs @@ -0,0 +1,109 @@ +defmodule MastaniServer.Test.Query.VideoViewer do + use MastaniServer.TestTools + + alias Helper.ORM + alias MastaniServer.CMS + + setup do + {:ok, community} = db_insert(:community) + {:ok, user} = db_insert(:user) + {:ok, video} = CMS.create_content(community, :video, mock_attrs(:video), user) + # noise + {:ok, video2} = CMS.create_content(community, :video, mock_attrs(:video), user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user) + + {:ok, ~m(user_conn guest_conn community video video2)a} + end + + @query """ + query($id: ID!) { + video(id: $id) { + views + } + } + """ + test "guest user views should +1 after query the video", ~m(guest_conn video)a do + variables = %{id: video.id} + views_1 = guest_conn |> query_result(@query, variables, "video") |> Map.get("views") + + variables = %{id: video.id} + views_2 = guest_conn |> query_result(@query, variables, "video") |> Map.get("views") + assert views_2 == views_1 + 1 + end + + test "login views should +1 after query the video", ~m(user_conn video)a do + variables = %{id: video.id} + views_1 = user_conn |> query_result(@query, variables, "video") |> Map.get("views") + + variables = %{id: video.id} + views_2 = user_conn |> query_result(@query, variables, "video") |> Map.get("views") + assert views_2 == views_1 + 1 + end + + test "login views be record only once in video viewers", ~m(video)a do + {:ok, user} = db_insert(:user) + user_conn = simu_conn(:user, user) + + assert {:error, _} = ORM.find_by(CMS.VideoViewer, %{video_id: video.id, user_id: user.id}) + + variables = %{id: video.id} + user_conn |> query_result(@query, variables, "video") |> Map.get("views") + assert {:ok, viewer} = ORM.find_by(CMS.VideoViewer, %{video_id: video.id, user_id: user.id}) + assert viewer.video_id == video.id + assert viewer.user_id == user.id + + variables = %{id: video.id} + user_conn |> query_result(@query, variables, "video") |> Map.get("views") + assert {:ok, _} = ORM.find_by(CMS.VideoViewer, %{video_id: video.id, user_id: user.id}) + assert viewer.video_id == video.id + assert viewer.user_id == user.id + end + + @paged_query """ + query($filter: PagedArticleFilter!) { + pagedVideos(filter: $filter) { + entries { + id + views + viewerHasViewed + } + } + } + """ + + @query """ + query($id: ID!) { + video(id: $id) { + id + views + viewerHasViewed + } + } + """ + test "user get has viewed flag after query/read the video", ~m(user_conn community video)a do + variables = %{filter: %{community: community.raw}} + results = user_conn |> query_result(@paged_query, variables, "pagedVideos") + found = Enum.find(results["entries"], &(&1["id"] == to_string(video.id))) + assert found["viewerHasViewed"] == false + + variables = %{id: video.id} + result = user_conn |> query_result(@query, variables, "video") + assert result["viewerHasViewed"] == true + + # noise: test viewer dataloader + {:ok, user2} = db_insert(:user) + user_conn2 = simu_conn(:user, user2) + variables = %{filter: %{community: community.raw}} + results = user_conn2 |> query_result(@paged_query, variables, "pagedVideos") + found = Enum.find(results["entries"], &(&1["id"] == to_string(video.id))) + assert found["viewerHasViewed"] == false + + variables = %{filter: %{community: community.raw}} + results = user_conn |> query_result(@paged_query, variables, "pagedVideos") + + found = Enum.find(results["entries"], &(&1["id"] == to_string(video.id))) + assert found["viewerHasViewed"] == true + end +end diff --git a/test/mastani_server_web/query/cms/videos_flags_test.exs b/test/mastani_server_web/query/cms/videos_flags_test.exs index 8436cd032..93d3d488f 100644 --- a/test/mastani_server_web/query/cms/videos_flags_test.exs +++ b/test/mastani_server_web/query/cms/videos_flags_test.exs @@ -61,12 +61,12 @@ defmodule MastaniServer.Test.Query.VideosFlags do assert results["pageSize"] == @page_size assert results["totalCount"] == @total_count - CMS.set_community_flags(%Video{id: video_m.id}, community.id, %{pin: true}) + {:ok, _pined_post} = CMS.pin_content(video_m, community) results = guest_conn |> query_result(@query, variables, "pagedVideos") entries_first = results["entries"] |> List.first() - assert results["totalCount"] == @total_count + assert results["totalCount"] == @total_count + 1 assert entries_first["id"] == to_string(video_m.id) assert entries_first["pin"] == true end diff --git a/test/mastani_server_web/query/cms/wiki_test.exs b/test/mastani_server_web/query/cms/wiki_test.exs new file mode 100644 index 000000000..df49c41b1 --- /dev/null +++ b/test/mastani_server_web/query/cms/wiki_test.exs @@ -0,0 +1,55 @@ +defmodule MastaniServer.Test.Query.Wiki do + use MastaniServer.TestTools + + alias MastaniServer.CMS + + setup do + {:ok, community} = db_insert(:community) + + wiki_attrs = mock_attrs(:wiki, %{community_id: community.id}) + {:ok, wiki} = CMS.sync_github_content(community, :wiki, wiki_attrs) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user) + + {:ok, ~m(user_conn guest_conn community wiki)a} + end + + @query """ + query($community: String!) { + wiki(community: $community) { + id + readme + contributors { + avatar + nickname + } + } + } + """ + test "basic graphql query on wiki", ~m(guest_conn community wiki)a do + variables = %{community: community.raw} + results = guest_conn |> query_result(@query, variables, "wiki") + + assert results["id"] == to_string(wiki.id) + assert is_valid_kv?(results, "readme", :string) + assert results["contributors"] |> length !== 0 + end + + @query """ + query($community: String!) { + wiki(community: $community) { + views + } + } + """ + test "views should +1 after query the wiki", ~m(guest_conn community)a do + variables = %{community: community.raw} + views_1 = guest_conn |> query_result(@query, variables, "wiki") |> Map.get("views") + + variables = %{community: community.raw} + views_2 = guest_conn |> query_result(@query, variables, "wiki") |> Map.get("views") + + assert views_2 == views_1 + 1 + end +end diff --git a/test/mastani_server_web/query/statistics/statistics_test.exs b/test/mastani_server_web/query/statistics/statistics_test.exs index 2b199e04a..3d6e685ab 100644 --- a/test/mastani_server_web/query/statistics/statistics_test.exs +++ b/test/mastani_server_web/query/statistics/statistics_test.exs @@ -5,6 +5,8 @@ defmodule MastaniServer.Test.Query.Statistics do alias MastaniServer.Statistics setup do + insert_geo_data() + {:ok, user} = db_insert(:user) guest_conn = simu_conn(:guest) @@ -30,4 +32,35 @@ defmodule MastaniServer.Test.Query.Statistics do assert ["count", "date"] == results |> List.first() |> Map.keys() end end + + @query """ + query { + citiesGeoInfo { + entries { + city + value + long + lant + } + totalCount + } + } + """ + describe "[statistics geo info]" do + test "should get cities geo infos", ~m(guest_conn)a do + result = guest_conn |> query_result(@query, %{}, "citiesGeoInfo") + assert result["entries"] == [] + assert result["totalCount"] == 0 + + {:ok, _} = Statistics.inc_count("成都") + {:ok, _} = Statistics.inc_count("成都") + {:ok, _} = Statistics.inc_count("广州") + + result = guest_conn |> query_result(@query, %{}, "citiesGeoInfo") + assert result["totalCount"] == 2 + + assert result["entries"] |> Enum.any?(&(&1["city"] == "成都")) + assert result["entries"] |> Enum.any?(&(&1["city"] == "广州")) + end + end end diff --git a/test/support/assert_helper.ex b/test/support/assert_helper.ex index bc4a3000e..7e3c69570 100644 --- a/test/support/assert_helper.ex +++ b/test/support/assert_helper.ex @@ -52,6 +52,17 @@ defmodule MastaniServer.Test.AssertHelper do is_valid_kv?(obj, "pageNumber", :int) end + def is_valid_pagination?(obj, :empty) when is_map(obj) do + case is_valid_pagination?(obj) do + false -> + false + + true -> + obj["entries"] |> Enum.empty?() and obj["totalCount"] == 0 and obj["pageNumber"] == 1 and + obj["totalPages"] == 1 + end + end + def is_valid_pagination?(obj, :raw) when is_map(obj) do is_valid_kv?(obj, "entries", :list) and is_valid_kv?(obj, "total_pages", :int) and is_valid_kv?(obj, "total_count", :int) and is_valid_kv?(obj, "page_size", :int) and @@ -80,6 +91,7 @@ defmodule MastaniServer.Test.AssertHelper do def mutation_get_error?(conn, query, variables) do conn |> post("/graphiql", query: query, variables: variables) + # |> IO.inspect(label: "debug status") |> json_response(200) # |> IO.inspect(label: "debug") |> Map.has_key?("errors") diff --git a/test/support/conn_simulator.ex b/test/support/conn_simulator.ex index aab260d58..73b68f431 100644 --- a/test/support/conn_simulator.ex +++ b/test/support/conn_simulator.ex @@ -2,7 +2,7 @@ defmodule MastaniServer.Test.ConnSimulator do @moduledoc """ mock user_conn, owner_conn, guest_conn """ - import MastaniServer.Factory + import MastaniServer.Support.Factory import Phoenix.ConnTest, only: [build_conn: 0] import Plug.Conn, only: [put_req_header: 3] diff --git a/test/support/factory.ex b/test/support/factory.ex index 2713a3aba..1d35d9f97 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -1,4 +1,4 @@ -defmodule MastaniServer.Factory do +defmodule MastaniServer.Support.Factory do @moduledoc """ This module defines the mock data/func to be used by tests that require insert some mock data to db. @@ -37,17 +37,13 @@ defmodule MastaniServer.Factory do desc: desc, duration: "03:30", duration_sec: Enum.random(300..12_000), - durationSec: Enum.random(300..12_000), source: "youtube", link: "http://www.youtube.com/video/1", - original_author: "simon", - originalAuthor: "simon", + original_author: "mydearxym", original_author_link: "http://www.youtube.com/user/1", - originalAuthorLink: "http://www.youtube.com/user/1", author: mock(:author), views: Enum.random(0..2000), publish_at: Timex.today() |> Timex.to_datetime(), - publishAt: "2017-11-01T12:00:00Z", communities: [ mock(:community), mock(:community) @@ -59,17 +55,29 @@ defmodule MastaniServer.Factory do desc = Faker.Lorem.sentence(%Range{first: 15, last: 60}) %{ - repo_name: Faker.Lorem.Shakespeare.king_richard_iii(), + title: Faker.Lorem.Shakespeare.king_richard_iii(), + owner_name: "coderplanets", + owner_url: "http://www.github.com/coderplanets", + repo_url: "http://www.github.com/coderplanets//coderplanets_server", desc: desc, + homepage_url: "http://www.github.com/coderplanets", readme: desc, - language: "javascript", + issues_count: Enum.random(0..2000), + prs_count: Enum.random(0..2000), + fork_count: Enum.random(0..2000), + star_count: Enum.random(0..2000), + watch_count: Enum.random(0..2000), + license: "MIT", + release_tag: "v22", + primary_language: %{ + name: "javascript", + color: "tomato" + }, + contributors: [ + mock_meta(:repo_contributor), + mock_meta(:repo_contributor) + ], author: mock(:author), - repo_link: "http://www.github.com/mydearxym", - producer: "mydearxym", - producer_link: "http://www.github.com/mydearxym", - repo_star_count: Enum.random(0..2000), - repo_fork_count: Enum.random(0..2000), - repo_watch_count: Enum.random(0..2000), views: Enum.random(0..2000), communities: [ mock(:community), @@ -78,6 +86,46 @@ defmodule MastaniServer.Factory do } end + defp mock_meta(:wiki) do + %{ + community: mock(:community), + readme: Faker.Lorem.sentence(%Range{first: 15, last: 60}), + last_sync: Timex.today() |> Timex.to_datetime(), + contributors: [ + mock_meta(:github_contributor), + mock_meta(:github_contributor), + mock_meta(:github_contributor) + ] + } + end + + defp mock_meta(:cheatsheet) do + mock_meta(:wiki) + end + + defp mock_meta(:repo_contributor) do + %{ + avatar: Faker.Avatar.image_url(), + html_url: Faker.Avatar.image_url(), + htmlUrl: Faker.Avatar.image_url(), + nickname: "mydearxym2" + } + end + + defp mock_meta(:github_contributor) do + unique_num = System.unique_integer([:positive, :monotonic]) + + %{ + github_id: "#{unique_num}-#{Faker.Lorem.sentence(%Range{first: 5, last: 10})}", + avatar: Faker.Avatar.image_url(), + html_url: Faker.Avatar.image_url(), + nickname: "mydearxym2", + bio: Faker.Lorem.sentence(%Range{first: 15, last: 60}), + location: "location #{unique_num}", + company: Faker.Company.name() + } + end + defp mock_meta(:job) do body = Faker.Lorem.sentence(%Range{first: 80, last: 120}) unique_num = System.unique_integer([:positive, :monotonic]) @@ -86,12 +134,18 @@ defmodule MastaniServer.Factory do title: Faker.Lorem.Shakespeare.king_richard_iii(), company: Faker.Company.name(), company_logo: Faker.Avatar.image_url(), - location: "location #{unique_num}", body: body, + desc: "活少, 美女多", digest: String.slice(body, 1, 150), length: String.length(body), author: mock(:author), views: Enum.random(0..2000), + salary: "20k-50k", + exp: "1-3年", + education: "master", + field: "互联网", + finance: "x轮", + scale: "300人", communities: [ mock(:community) ] @@ -221,6 +275,12 @@ defmodule MastaniServer.Factory do def mock_attrs(:thread, attrs), do: mock_meta(:thread) |> Map.merge(attrs) def mock_attrs(:mention, attrs), do: mock_meta(:mention) |> Map.merge(attrs) + def mock_attrs(:wiki, attrs), do: mock_meta(:wiki) |> Map.merge(attrs) + def mock_attrs(:cheatsheet, attrs), do: mock_meta(:cheatsheet) |> Map.merge(attrs) + + def mock_attrs(:github_contributor, attrs), + do: mock_meta(:github_contributor) |> Map.merge(attrs) + def mock_attrs(:communities_threads, attrs), do: mock_meta(:communities_threads) |> Map.merge(attrs) @@ -241,6 +301,8 @@ defmodule MastaniServer.Factory do defp mock(:video), do: CMS.Video |> struct(mock_meta(:video)) defp mock(:repo), do: CMS.Repo |> struct(mock_meta(:repo)) defp mock(:job), do: CMS.Job |> struct(mock_meta(:job)) + defp mock(:wiki), do: CMS.CommunityWiki |> struct(mock_meta(:wiki)) + defp mock(:cheatsheet), do: CMS.CommunityCheatsheet |> struct(mock_meta(:cheatsheet)) defp mock(:comment), do: CMS.Comment |> struct(mock_meta(:comment)) defp mock(:mention), do: Delivery.Mention |> struct(mock_meta(:mention)) defp mock(:author), do: CMS.Author |> struct(mock_meta(:author)) @@ -320,4 +382,19 @@ defmodule MastaniServer.Factory do {:ok, _} = Delivery.notify_someone(u, user, info) end) end + + alias MastaniServer.Statistics.UserGeoInfo + alias MastaniServer.Support.GeoData + + alias Helper.ORM + + def insert_geo_data do + # IO.inspect GeoData.all, label: "hello" + GeoData.all() + |> Enum.each(fn info -> + # IO.inspect info, label: "inserting" + # UserGeoInfo |> Repo.insert(info) + UserGeoInfo |> ORM.create(info) + end) + end end diff --git a/test/support/geo_data.ex b/test/support/geo_data.ex new file mode 100644 index 000000000..12ebc7d26 --- /dev/null +++ b/test/support/geo_data.ex @@ -0,0 +1,1149 @@ +defmodule MastaniServer.Support.GeoData do + @moduledoc """ + return geo data + """ + def all do + [ + %{ + city: "海门", + long: 121.15, + lant: 31.89, + value: 0 + }, + %{ + city: "鄂尔多斯", + long: 109.781327, + lant: 39.608266, + value: 0 + }, + %{ + city: "招远", + long: 120.38, + lant: 37.35, + value: 0 + }, + %{ + city: "舟山", + long: 122.207216, + lant: 29.985295, + value: 0 + }, + %{ + city: "齐齐哈尔", + long: 123.97, + lant: 47.33, + value: 0 + }, + %{ + city: "盐城", + long: 120.13, + lant: 33.38, + value: 0 + }, + %{ + city: "赤峰", + long: 118.87, + lant: 42.28, + value: 0 + }, + %{ + city: "青岛", + long: 120.33, + lant: 36.07, + value: 0 + }, + %{ + city: "乳山", + long: 121.52, + lant: 36.89, + value: 0 + }, + %{ + city: "金昌", + long: 102.188043, + lant: 38.520089, + value: 0 + }, + %{ + city: "泉州", + long: 118.58, + lant: 24.93, + value: 0 + }, + %{ + city: "莱西", + long: 120.53, + lant: 36.86, + value: 0 + }, + %{ + city: "日照", + long: 119.46, + lant: 35.42, + value: 0 + }, + %{ + city: "胶南", + long: 119.97, + lant: 35.88, + value: 0 + }, + %{ + city: "南通", + long: 121.05, + lant: 32.08, + value: 0 + }, + %{ + city: "拉萨", + long: 91.11, + lant: 29.97, + value: 0 + }, + %{ + city: "云浮", + long: 112.02, + lant: 22.93, + value: 0 + }, + %{ + city: "梅州", + long: 116.1, + lant: 24.55, + value: 0 + }, + %{ + city: "文登", + long: 122.05, + lant: 37.2, + value: 0 + }, + %{ + city: "上海", + long: 121.48, + lant: 31.22, + value: 0 + }, + %{ + city: "攀枝花", + long: 101.718637, + lant: 26.582347, + value: 0 + }, + %{ + city: "威海", + long: 122.1, + lant: 37.5, + value: 0 + }, + %{ + city: "承德", + long: 117.93, + lant: 40.97, + value: 0 + }, + %{ + city: "厦门", + long: 118.1, + lant: 24.46, + value: 0 + }, + %{ + city: "汕尾", + long: 115.375279, + lant: 22.786211, + value: 0 + }, + %{ + city: "潮州", + long: 116.63, + lant: 23.68, + value: 0 + }, + %{ + city: "丹东", + long: 124.37, + lant: 40.13, + value: 0 + }, + %{ + city: "太仓", + long: 121.1, + lant: 31.45, + value: 0 + }, + %{ + city: "曲靖", + long: 103.79, + lant: 25.51, + value: 0 + }, + %{ + city: "烟台", + long: 121.39, + lant: 37.52, + value: 0 + }, + %{ + city: "福州", + long: 119.3, + lant: 26.08, + value: 0 + }, + %{ + city: "瓦房店", + long: 121.979603, + lant: 39.627114, + value: 0 + }, + %{ + city: "即墨", + long: 120.45, + lant: 36.38, + value: 0 + }, + %{ + city: "抚顺", + long: 123.97, + lant: 41.97, + value: 0 + }, + %{ + city: "玉溪", + long: 102.52, + lant: 24.35, + value: 0 + }, + %{ + city: "张家口", + long: 114.87, + lant: 40.82, + value: 0 + }, + %{ + city: "阳泉", + long: 113.57, + lant: 37.85, + value: 0 + }, + %{ + city: "莱州", + long: 119.942327, + lant: 37.177017, + value: 0 + }, + %{ + city: "湖州", + long: 120.1, + lant: 30.86, + value: 0 + }, + %{ + city: "汕头", + long: 116.69, + lant: 23.39, + value: 0 + }, + %{ + city: "昆山", + long: 120.95, + lant: 31.39, + value: 0 + }, + %{ + city: "宁波", + long: 121.56, + lant: 29.86, + value: 0 + }, + %{ + city: "湛江", + long: 110.359377, + lant: 21.270708, + value: 0 + }, + %{ + city: "揭阳", + long: 116.35, + lant: 23.55, + value: 0 + }, + %{ + city: "荣成", + long: 122.41, + lant: 37.16, + value: 0 + }, + %{ + city: "连云港", + long: 119.16, + lant: 34.59, + value: 0 + }, + %{ + city: "葫芦岛", + long: 120.836932, + lant: 40.711052, + value: 0 + }, + %{ + city: "常熟", + long: 120.74, + lant: 31.64, + value: 0 + }, + %{ + city: "东莞", + long: 113.75, + lant: 23.04, + value: 0 + }, + %{ + city: "河源", + long: 114.68, + lant: 23.73, + value: 0 + }, + %{ + city: "淮安", + long: 119.15, + lant: 33.5, + value: 0 + }, + %{ + city: "泰州", + long: 119.9, + lant: 32.49, + value: 0 + }, + %{ + city: "南宁", + long: 108.33, + lant: 22.84, + value: 0 + }, + %{ + city: "营口", + long: 122.18, + lant: 40.65, + value: 0 + }, + %{ + city: "惠州", + long: 114.4, + lant: 23.09, + value: 0 + }, + %{ + city: "江阴", + long: 120.26, + lant: 31.91, + value: 0 + }, + %{ + city: "蓬莱", + long: 120.75, + lant: 37.8, + value: 0 + }, + %{ + city: "韶关", + long: 113.62, + lant: 24.84, + value: 0 + }, + %{ + city: "嘉峪关", + long: 98.289152, + lant: 39.77313, + value: 0 + }, + %{ + city: "广州", + long: 113.23, + lant: 23.16, + value: 0 + }, + %{ + city: "延安", + long: 109.47, + lant: 36.6, + value: 0 + }, + %{ + city: "太原", + long: 112.53, + lant: 37.87, + value: 0 + }, + %{ + city: "清远", + long: 113.01, + lant: 23.7, + value: 0 + }, + %{ + city: "中山", + long: 113.38, + lant: 22.52, + value: 0 + }, + %{ + city: "昆明", + long: 102.73, + lant: 25.04, + value: 0 + }, + %{ + city: "寿光", + long: 118.73, + lant: 36.86, + value: 0 + }, + %{ + city: "盘锦", + long: 122.070714, + lant: 41.119997, + value: 0 + }, + %{ + city: "长治", + long: 113.08, + lant: 36.18, + value: 0 + }, + %{ + city: "深圳", + long: 114.07, + lant: 22.62, + value: 0 + }, + %{ + city: "珠海", + long: 113.52, + lant: 22.3, + value: 0 + }, + %{ + city: "宿迁", + long: 118.3, + lant: 33.96, + value: 0 + }, + %{ + city: "咸阳", + long: 108.72, + lant: 34.36, + value: 0 + }, + %{ + city: "铜川", + long: 109.11, + lant: 35.09, + value: 0 + }, + %{ + city: "平度", + long: 119.97, + lant: 36.77, + value: 0 + }, + %{ + city: "佛山", + long: 113.11, + lant: 23.05, + value: 0 + }, + %{ + city: "海口", + long: 110.35, + lant: 20.02, + value: 0 + }, + %{ + city: "江门", + long: 113.06, + lant: 22.61, + value: 0 + }, + %{ + city: "章丘", + long: 117.53, + lant: 36.72, + value: 0 + }, + %{ + city: "肇庆", + long: 112.44, + lant: 23.05, + value: 0 + }, + %{ + city: "大连", + long: 121.62, + lant: 38.92, + value: 0 + }, + %{ + city: "临汾", + long: 111.5, + lant: 36.08, + value: 0 + }, + %{ + city: "吴江", + long: 120.63, + lant: 31.16, + value: 0 + }, + %{ + city: "石嘴山", + long: 106.39, + lant: 39.04, + value: 0 + }, + %{ + city: "沈阳", + long: 123.38, + lant: 41.8, + value: 0 + }, + %{ + city: "苏州", + long: 120.62, + lant: 31.32, + value: 0 + }, + %{ + city: "茂名", + long: 110.88, + lant: 21.68, + value: 0 + }, + %{ + city: "嘉兴", + long: 120.76, + lant: 30.77, + value: 0 + }, + %{ + city: "长春", + long: 125.35, + lant: 43.88, + value: 0 + }, + %{ + city: "胶州", + long: 120.03336, + lant: 36.264622, + value: 0 + }, + %{ + city: "银川", + long: 106.27, + lant: 38.47, + value: 0 + }, + %{ + city: "张家港", + long: 120.555821, + lant: 31.875428, + value: 0 + }, + %{ + city: "三门峡", + long: 111.19, + lant: 34.76, + value: 0 + }, + %{ + city: "锦州", + long: 121.15, + lant: 41.13, + value: 0 + }, + %{ + city: "南昌", + long: 115.89, + lant: 28.68, + value: 0 + }, + %{ + city: "柳州", + long: 109.4, + lant: 24.33, + value: 0 + }, + %{ + city: "三亚", + long: 109.511909, + lant: 18.252847, + value: 0 + }, + %{ + city: "自贡", + long: 104.778442, + lant: 29.33903, + value: 0 + }, + %{ + city: "吉林", + long: 126.57, + lant: 43.87, + value: 0 + }, + %{ + city: "阳江", + long: 111.95, + lant: 21.85, + value: 0 + }, + %{ + city: "泸州", + long: 105.39, + lant: 28.91, + value: 0 + }, + %{ + city: "西宁", + long: 101.74, + lant: 36.56, + value: 0 + }, + %{ + city: "宜宾", + long: 104.56, + lant: 29.77, + value: 0 + }, + %{ + city: "呼和浩特", + long: 111.65, + lant: 40.82, + value: 0 + }, + %{ + city: "成都", + long: 104.06, + lant: 30.67, + value: 0 + }, + %{ + city: "大同", + long: 113.3, + lant: 40.12, + value: 0 + }, + %{ + city: "镇江", + long: 119.44, + lant: 32.2, + value: 0 + }, + %{ + city: "桂林", + long: 110.28, + lant: 25.29, + value: 0 + }, + %{ + city: "张家界", + long: 110.479191, + lant: 29.117096, + value: 0 + }, + %{ + city: "宜兴", + long: 119.82, + lant: 31.36, + value: 0 + }, + %{ + city: "北海", + long: 109.12, + lant: 21.49, + value: 0 + }, + %{ + city: "西安", + long: 108.95, + lant: 34.27, + value: 0 + }, + %{ + city: "金坛", + long: 119.56, + lant: 31.74, + value: 0 + }, + %{ + city: "东营", + long: 118.49, + lant: 37.46, + value: 0 + }, + %{ + city: "牡丹江", + long: 129.58, + lant: 44.6, + value: 0 + }, + %{ + city: "遵义", + long: 106.9, + lant: 27.7, + value: 0 + }, + %{ + city: "绍兴", + long: 120.58, + lant: 30.01, + value: 0 + }, + %{ + city: "扬州", + long: 119.42, + lant: 32.39, + value: 0 + }, + %{ + city: "常州", + long: 119.95, + lant: 31.79, + value: 0 + }, + %{ + city: "潍坊", + long: 119.1, + lant: 36.62, + value: 0 + }, + %{ + city: "重庆", + long: 106.54, + lant: 29.59, + value: 0 + }, + %{ + city: "台州", + long: 121.420757, + lant: 28.656386, + value: 0 + }, + %{ + city: "南京", + long: 118.78, + lant: 32.04, + value: 0 + }, + %{ + city: "滨州", + long: 118.03, + lant: 37.36, + value: 0 + }, + %{ + city: "贵阳", + long: 106.71, + lant: 26.57, + value: 0 + }, + %{ + city: "无锡", + long: 120.29, + lant: 31.59, + value: 0 + }, + %{ + city: "本溪", + long: 123.73, + lant: 41.3, + value: 0 + }, + %{ + city: "克拉玛依", + long: 84.77, + lant: 45.59, + value: 0 + }, + %{ + city: "渭南", + long: 109.5, + lant: 34.52, + value: 0 + }, + %{ + city: "马鞍山", + long: 118.48, + lant: 31.56, + value: 0 + }, + %{ + city: "宝鸡", + long: 107.15, + lant: 34.38, + value: 0 + }, + %{ + city: "焦作", + long: 113.21, + lant: 35.24, + value: 0 + }, + %{ + city: "句容", + long: 119.16, + lant: 31.95, + value: 0 + }, + %{ + city: "北京", + long: 116.46, + lant: 39.92, + value: 0 + }, + %{ + city: "徐州", + long: 117.2, + lant: 34.26, + value: 0 + }, + %{ + city: "衡水", + long: 115.72, + lant: 37.72, + value: 0 + }, + %{ + city: "包头", + long: 110, + lant: 40.58, + value: 0 + }, + %{ + city: "绵阳", + long: 104.73, + lant: 31.48, + value: 0 + }, + %{ + city: "乌鲁木齐", + long: 87.68, + lant: 43.77, + value: 0 + }, + %{ + city: "枣庄", + long: 117.57, + lant: 34.86, + value: 0 + }, + %{ + city: "杭州", + long: 120.19, + lant: 30.26, + value: 0 + }, + %{ + city: "淄博", + long: 118.05, + lant: 36.78, + value: 0 + }, + %{ + city: "鞍山", + long: 122.85, + lant: 41.12, + value: 0 + }, + %{ + city: "溧阳", + long: 119.48, + lant: 31.43, + value: 0 + }, + %{ + city: "库尔勒", + long: 86.06, + lant: 41.68, + value: 0 + }, + %{ + city: "安阳", + long: 114.35, + lant: 36.1, + value: 0 + }, + %{ + city: "开封", + long: 114.35, + lant: 34.79, + value: 0 + }, + %{ + city: "济南", + long: 117, + lant: 36.65, + value: 0 + }, + %{ + city: "德阳", + long: 104.37, + lant: 31.13, + value: 0 + }, + %{ + city: "温州", + long: 120.65, + lant: 28.01, + value: 0 + }, + %{ + city: "九江", + long: 115.97, + lant: 29.71, + value: 0 + }, + %{ + city: "邯郸", + long: 114.47, + lant: 36.6, + value: 0 + }, + %{ + city: "临安", + long: 119.72, + lant: 30.23, + value: 0 + }, + %{ + city: "兰州", + long: 103.73, + lant: 36.03, + value: 0 + }, + %{ + city: "沧州", + long: 116.83, + lant: 38.33, + value: 0 + }, + %{ + city: "临沂", + long: 118.35, + lant: 35.05, + value: 0 + }, + %{ + city: "南充", + long: 106.110698, + lant: 30.837793, + value: 0 + }, + %{ + city: "天津", + long: 117.2, + lant: 39.13, + value: 0 + }, + %{ + city: "富阳", + long: 119.95, + lant: 30.07, + value: 0 + }, + %{ + city: "泰安", + long: 117.13, + lant: 36.18, + value: 0 + }, + %{ + city: "诸暨", + long: 120.23, + lant: 29.71, + value: 0 + }, + %{ + city: "郑州", + long: 113.65, + lant: 34.76, + value: 0 + }, + %{ + city: "哈尔滨", + long: 126.63, + lant: 45.75, + value: 0 + }, + %{ + city: "聊城", + long: 115.97, + lant: 36.45, + value: 0 + }, + %{ + city: "芜湖", + long: 118.38, + lant: 31.33, + value: 0 + }, + %{ + city: "唐山", + long: 118.02, + lant: 39.63, + value: 0 + }, + %{ + city: "平顶山", + long: 113.29, + lant: 33.75, + value: 0 + }, + %{ + city: "邢台", + long: 114.48, + lant: 37.05, + value: 0 + }, + %{ + city: "德州", + long: 116.29, + lant: 37.45, + value: 0 + }, + %{ + city: "济宁", + long: 116.59, + lant: 35.38, + value: 0 + }, + %{ + city: "荆州", + long: 112.239741, + lant: 30.335165, + value: 0 + }, + %{ + city: "宜昌", + long: 111.3, + lant: 30.7, + value: 0 + }, + %{ + city: "义乌", + long: 120.06, + lant: 29.32, + value: 0 + }, + %{ + city: "丽水", + long: 119.92, + lant: 28.45, + value: 0 + }, + %{ + city: "洛阳", + long: 112.44, + lant: 34.7, + value: 0 + }, + %{ + city: "秦皇岛", + long: 119.57, + lant: 39.95, + value: 0 + }, + %{ + city: "株洲", + long: 113.16, + lant: 27.83, + value: 0 + }, + %{ + city: "石家庄", + long: 114.48, + lant: 38.03, + value: 0 + }, + %{ + city: "莱芜", + long: 117.67, + lant: 36.19, + value: 0 + }, + %{ + city: "常德", + long: 111.69, + lant: 29.05, + value: 0 + }, + %{ + city: "保定", + long: 115.48, + lant: 38.85, + value: 0 + }, + %{ + city: "湘潭", + long: 112.91, + lant: 27.87, + value: 0 + }, + %{ + city: "金华", + long: 119.64, + lant: 29.12, + value: 0 + }, + %{ + city: "岳阳", + long: 113.09, + lant: 29.37, + value: 0 + }, + %{ + city: "长沙", + long: 113, + lant: 28.21, + value: 0 + }, + %{ + city: "衢州", + long: 118.88, + lant: 28.97, + value: 0 + }, + %{ + city: "廊坊", + long: 116.7, + lant: 39.53, + value: 0 + }, + %{ + city: "菏泽", + long: 115.480656, + lant: 35.23375, + value: 0 + }, + %{ + city: "合肥", + long: 117.27, + lant: 31.86, + value: 0 + }, + %{ + city: "武汉", + long: 114.31, + lant: 30.52, + value: 0 + }, + %{ + city: "大庆", + long: 125.03, + lant: 46.58, + value: 0 + } + ] + end +end diff --git a/test/support/test_tools.ex b/test/support/test_tools.ex index cc640ffb1..0b0d84218 100644 --- a/test/support/test_tools.ex +++ b/test/support/test_tools.ex @@ -8,11 +8,12 @@ defmodule MastaniServer.TestTools do quote do use MastaniServerWeb.ConnCase, async: true - import MastaniServer.Factory + import MastaniServer.Support.Factory import MastaniServer.Test.ConnSimulator import MastaniServer.Test.AssertHelper import Ecto.Query, warn: false import Helper.ErrorCode + import Helper.Utils, only: [camelize_map_key: 1] import ShortMaps end