diff --git a/lib/groupher_server/cms/cms.ex b/lib/groupher_server/cms/cms.ex index be69a21b5..192f2eef4 100644 --- a/lib/groupher_server/cms/cms.ex +++ b/lib/groupher_server/cms/cms.ex @@ -11,6 +11,7 @@ defmodule GroupherServer.CMS do AbuseReport, ArticleCURD, BlogCURD, + WorksCURD, ArticleCommunity, ArticleEmotion, CitedArtiment, @@ -108,6 +109,9 @@ defmodule GroupherServer.CMS do defdelegate update_blog_rss(attrs), to: BlogCURD defdelegate blog_rss_info(rss), to: BlogCURD + defdelegate create_works(attrs, user), to: WorksCURD + defdelegate update_works(attrs, user), to: WorksCURD + defdelegate paged_citing_contents(type, id, filter), to: CitedArtiment defdelegate upvote_article(thread, article_id, user), to: ArticleUpvote diff --git a/lib/groupher_server/cms/delegates/article_curd.ex b/lib/groupher_server/cms/delegates/article_curd.ex index 1366ca5ce..c4ae8d21a 100644 --- a/lib/groupher_server/cms/delegates/article_curd.ex +++ b/lib/groupher_server/cms/delegates/article_curd.ex @@ -13,7 +13,8 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do module_to_atom: 1, get_config: 2, ensure: 2, - module_to_upcase: 1 + module_to_upcase: 1, + atom_values_to_upcase: 1 ] import GroupherServer.CMS.Delegate.Helper, only: [mark_viewer_emotion_states: 2, thread_of: 1] @@ -189,6 +190,8 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do {:ok, %Post{}} """ def create_article(%Community{id: cid}, thread, attrs, %User{id: uid}) do + attrs = atom_values_to_upcase(attrs) + with {:ok, author} <- ensure_author_exists(%User{id: uid}), {:ok, info} <- match(thread), {:ok, community} <- ORM.find(Community, cid) do @@ -258,6 +261,8 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do do: raise_error(:archived, "article is archived, can not be edit or delete") def update_article(article, attrs) do + attrs = atom_values_to_upcase(attrs) + Multi.new() |> Multi.run(:update_article, fn _, _ -> do_update_article(article, attrs) diff --git a/lib/groupher_server/cms/delegates/blog_curd.ex b/lib/groupher_server/cms/delegates/blog_curd.ex index beeea75a0..c8752668d 100644 --- a/lib/groupher_server/cms/delegates/blog_curd.ex +++ b/lib/groupher_server/cms/delegates/blog_curd.ex @@ -3,7 +3,7 @@ defmodule GroupherServer.CMS.Delegate.BlogCURD do CURD operation on post/job ... """ import Ecto.Query, warn: false - import Helper.Utils, only: [strip_struct: 1, done: 1] + import Helper.Utils, only: [strip_struct: 1] import Helper.ErrorCode import GroupherServer.CMS.Delegate.ArticleCURD, only: [create_article: 4] @@ -38,17 +38,7 @@ defmodule GroupherServer.CMS.Delegate.BlogCURD do ## 1.2 如不存在,则创建一条 RSS with {:ok, feed} <- blog_rss_info(attrs.rss) do do_create_blog(community, attrs, user, feed) - - # IO.inspect(feed, label: "create blog") - # 通过 feed 有没有 id 来 insert / update - # 通过 blog_title, 组合 attrs 传给 create_article end - - # 2. 创建 blog - ## 2.1 blog +字段 rss, author - ## 2.2 title, digest, xxx - - # 前台获取作者信息的时候从 rss 表读取 end # rss 记录存在, 直接创建 blog @@ -102,19 +92,6 @@ defmodule GroupherServer.CMS.Delegate.BlogCURD do end end - # create done - # defp result({:ok, %{set_active_at_timestamp: result}}) do - # {:ok, result} - # end - - # defp result({:ok, %{update_article_meta: result}}), do: {:ok, result} - - # defp result({:error, :create_article, _result, _steps}) do - # {:error, [message: "create article", code: ecode(:create_fails)]} - # end - - # defp result({:error, _, result, _steps}), do: {:error, result} - @doc """ get and cache feed by rss address as key """ diff --git a/lib/groupher_server/cms/delegates/works_curd.ex b/lib/groupher_server/cms/delegates/works_curd.ex new file mode 100644 index 000000000..f46a66c78 --- /dev/null +++ b/lib/groupher_server/cms/delegates/works_curd.ex @@ -0,0 +1,160 @@ +defmodule GroupherServer.CMS.Delegate.WorksCURD do + @moduledoc """ + CURD operation on post/job ... + """ + import Ecto.Query, warn: false + import Helper.Utils, only: [done: 1, atom_values_to_upcase: 1] + import Helper.ErrorCode + + import GroupherServer.CMS.Delegate.ArticleCURD, only: [create_article: 4, update_article: 2] + + alias GroupherServer.{Accounts, CMS, Repo} + alias CMS.Model.{Community, Techstack, City, Works} + alias Accounts.Model.User + + alias Helper.ORM + alias Ecto.Multi + + # works can only be published on home community + def create_works(attrs, %User{} = user) do + attrs = attrs |> atom_values_to_upcase + + with {:ok, home_community} <- ORM.find_by(Community, %{raw: "home"}) do + Multi.new() + |> Multi.run(:create_works, fn _, _ -> + create_article(home_community, :works, attrs, user) + end) + |> Multi.run(:update_works_fields, fn _, %{create_works: works} -> + update_works_fields(works, attrs) + end) + |> Repo.transaction() + |> result() + end + end + + def update_works(%Works{} = works, attrs) do + attrs = attrs |> atom_values_to_upcase + + Multi.new() + |> Multi.run(:update_works_fields, fn _, _ -> + update_works_fields(works, attrs) + end) + |> Multi.run(:update_works, fn _, %{update_works_fields: works} -> + update_article(works, attrs) + end) + |> Repo.transaction() + |> result() + end + + # update works spec fields + defp update_works_fields(%Works{} = works, attrs) do + techstacks = Map.get(attrs, :techstacks, []) + cities = Map.get(attrs, :cities, []) + social_info = Map.get(attrs, :social_info, []) + app_store = Map.get(attrs, :app_store, []) + + with {:ok, techstacks} <- get_or_create_techstacks(techstacks), + {:ok, cities} <- get_or_create_cities(cities) do + works = Repo.preload(works, [:techstacks, :cities]) + + works + |> Ecto.Changeset.change() + |> Ecto.Changeset.put_assoc(:techstacks, works.techstacks ++ techstacks) + |> Ecto.Changeset.put_assoc(:cities, works.cities ++ cities) + |> Ecto.Changeset.put_embed(:social_info, social_info) + |> Ecto.Changeset.put_embed(:app_store, app_store) + |> Repo.update() + end + end + + defp get_or_create_cities([]), do: {:ok, []} + + defp get_or_create_cities(cities) do + cities + |> Enum.map(&String.downcase(&1)) + |> Enum.reduce([], fn title, acc -> + with {:ok, city} <- get_city(title) do + acc ++ [city] + end + end) + |> done + end + + defp get_city(title) do + case ORM.find_by(City, %{title: title}) do + {:error, _} -> create_city(title) + {:ok, city} -> {:ok, city} + end + end + + defp create_city(title) do + attrs = + case ORM.find_by(Community, %{raw: title}) do + {:ok, community} -> + %{ + title: community.title, + logo: community.logo, + desc: community.desc, + link: "/#{community.raw}" + } + + {:error, _} -> + %{title: title} + end + + ORM.create(City, attrs) + end + + defp get_or_create_techstacks([]), do: {:ok, []} + + defp get_or_create_techstacks(techstacks) do + techstacks + |> Enum.map(&String.downcase(&1)) + |> Enum.reduce([], fn title, acc -> + with {:ok, techstack} <- get_techstack(title) do + acc ++ [techstack] + end + end) + |> done + end + + defp get_techstack(title) do + case ORM.find_by(Techstack, %{title: title}) do + {:error, _} -> create_techstack(title) + {:ok, techstack} -> {:ok, techstack} + end + end + + defp create_techstack(title) do + attrs = + case ORM.find_by(Community, %{raw: title}) do + {:ok, community} -> + %{ + title: community.title, + logo: community.logo, + community_link: "/#{community.raw}", + desc: community.desc + } + + {:error, _} -> + %{title: title} + end + + ORM.create(Techstack, attrs) + end + + defp result({:ok, %{create_works: result}}), do: {:ok, result} + defp result({:ok, %{update_works: result}}), do: {:ok, result} + + defp result({:error, :create_works, _result, _steps}) do + {:error, [message: "create works", code: ecode(:create_fails)]} + end + + defp result({:error, :update_works_fields, _result, _steps}) do + {:error, [message: "update works fields", code: ecode(:create_fails)]} + end + + defp result({:error, :update_works, _result, _steps}) do + {:error, [message: "update works", code: ecode(:update_fails)]} + end +end diff --git a/lib/groupher_server/cms/models/city.ex b/lib/groupher_server/cms/models/city.ex new file mode 100644 index 000000000..76d1ed06c --- /dev/null +++ b/lib/groupher_server/cms/models/city.ex @@ -0,0 +1,47 @@ +defmodule GroupherServer.CMS.Model.City do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + use Accessible + + import Ecto.Changeset + + # alias GroupherServer.CMS + + @timestamps_opts [type: :utc_datetime_usec] + + @required_fields ~w(title)a + @optional_fields ~w(logo desc link)a + + @type t :: %City{} + schema "cms_cities" do + ## mailstone + field(:title, :string) + field(:logo, :string) + field(:desc, :string) + field(:link, :string) + + timestamps() + end + + @doc false + def changeset(%City{} = city, attrs) do + city + |> cast(attrs, @optional_fields ++ @required_fields) + |> validate_required(@required_fields) + |> generl_changeset + end + + @doc false + def update_changeset(%City{} = city, attrs) do + city + |> cast(attrs, @optional_fields ++ @required_fields) + |> generl_changeset + end + + defp generl_changeset(changeset) do + changeset + |> validate_length(:title, min: 1, max: 100) + end +end diff --git a/lib/groupher_server/cms/models/embeds/app_store.ex b/lib/groupher_server/cms/models/embeds/app_store.ex new file mode 100644 index 000000000..a03240ab5 --- /dev/null +++ b/lib/groupher_server/cms/models/embeds/app_store.ex @@ -0,0 +1,23 @@ +defmodule GroupherServer.CMS.Model.Embeds.AppStore do + @moduledoc """ + general community meta + """ + use Ecto.Schema + use Accessible + + import Ecto.Changeset + + @required_fields ~w(platform)a + @optional_fields ~w(link)a + + embedded_schema do + field(:platform, :string) + field(:link, :string) + end + + def changeset(struct, attrs) do + struct + |> cast(attrs, @optional_fields ++ @required_fields) + |> validate_required(@required_fields) + end +end diff --git a/lib/groupher_server/cms/models/embeds/social_info.ex b/lib/groupher_server/cms/models/embeds/social_info.ex new file mode 100644 index 000000000..5ed21f035 --- /dev/null +++ b/lib/groupher_server/cms/models/embeds/social_info.ex @@ -0,0 +1,23 @@ +defmodule GroupherServer.CMS.Model.Embeds.SocialInfo do + @moduledoc """ + general community meta + """ + use Ecto.Schema + use Accessible + + import Ecto.Changeset + + @required_fields ~w(platform)a + @optional_fields ~w(link)a + + embedded_schema do + field(:platform, :string) + field(:link, :string) + end + + def changeset(struct, attrs) do + struct + |> cast(attrs, @optional_fields ++ @required_fields) + |> validate_required(@required_fields) + end +end diff --git a/lib/groupher_server/cms/models/techstack.ex b/lib/groupher_server/cms/models/techstack.ex new file mode 100644 index 000000000..5286ef95a --- /dev/null +++ b/lib/groupher_server/cms/models/techstack.ex @@ -0,0 +1,50 @@ +defmodule GroupherServer.CMS.Model.Techstack do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + use Accessible + + import Ecto.Changeset + + # alias GroupherServer.CMS + + @timestamps_opts [type: :utc_datetime_usec] + + @required_fields ~w(title)a + @optional_fields ~w(logo desc home_link community_link category)a + + @type t :: %Techstack{} + schema "cms_techstacks" do + ## mailstone + field(:title, :string) + field(:logo, :string) + field(:desc, :string) + + field(:home_link, :string) + field(:community_link, :string) + field(:category, :string) + + timestamps() + end + + @doc false + def changeset(%Techstack{} = techstack, attrs) do + techstack + |> cast(attrs, @optional_fields ++ @required_fields) + |> validate_required(@required_fields) + |> generl_changeset + end + + @doc false + def update_changeset(%Techstack{} = techstack, attrs) do + techstack + |> cast(attrs, @optional_fields ++ @required_fields) + |> generl_changeset + end + + defp generl_changeset(changeset) do + changeset + |> validate_length(:title, min: 1, max: 100) + end +end diff --git a/lib/groupher_server/cms/models/works.ex b/lib/groupher_server/cms/models/works.ex index 56f3deee8..01fb3157c 100644 --- a/lib/groupher_server/cms/models/works.ex +++ b/lib/groupher_server/cms/models/works.ex @@ -9,16 +9,45 @@ defmodule GroupherServer.CMS.Model.Works do import GroupherServer.CMS.Helper.Macros alias GroupherServer.CMS - alias CMS.Model.Embeds + alias CMS.Model.{Embeds, Techstack, City} @timestamps_opts [type: :utc_datetime_usec] @required_fields ~w(title digest)a @article_cast_fields general_article_cast_fields() - @optional_fields @article_cast_fields + @optional_fields ~w(home_link profit_mode working_mode community_link interview_link)a ++ + @article_cast_fields @type t :: %Works{} schema "cms_works" do + ## mailstone + field(:home_link, :string) + # ... + field(:profit_mode, :string) + # fulltime / parttime + field(:working_mode, :string) + + embeds_many(:social_info, Embeds.SocialInfo, on_replace: :delete) + embeds_many(:app_store, Embeds.AppStore, on_replace: :delete) + # embeds_many(:teamate, Embeds.Teammate, on_replace: :delete) + + field(:community_link, :string) + field(:interview_link, :string) + + many_to_many( + :techstacks, + Techstack, + join_through: "works_join_techstacks", + on_replace: :delete + ) + + many_to_many( + :cities, + City, + join_through: "works_join_cities", + on_replace: :delete + ) + article_tags_field(:works) article_communities_field(:works) general_article_fields(:works) @@ -30,6 +59,8 @@ defmodule GroupherServer.CMS.Model.Works do |> cast(attrs, @optional_fields ++ @required_fields) |> validate_required(@required_fields) |> cast_embed(:meta, required: false, with: &Embeds.ArticleMeta.changeset/2) + |> cast_embed(:social_info, required: false, with: &Embeds.SocialInfo.changeset/2) + |> cast_embed(:app_store, required: false, with: &Embeds.AppStore.changeset/2) |> generl_changeset end diff --git a/lib/groupher_server_web/resolvers/cms_resolver.ex b/lib/groupher_server_web/resolvers/cms_resolver.ex index 58be1c35c..8d35d0d6b 100644 --- a/lib/groupher_server_web/resolvers/cms_resolver.ex +++ b/lib/groupher_server_web/resolvers/cms_resolver.ex @@ -70,6 +70,14 @@ defmodule GroupherServerWeb.Resolvers.CMS do 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 create_works(_root, args, %{context: %{cur_user: user}}) do + CMS.create_works(args, user) + end + + def update_works(_root, %{passport_source: works} = args, _info) do + CMS.update_works(works, args) + end + def create_article(_root, ~m(community_id thread)a = args, %{context: %{cur_user: user}}) do CMS.create_article(%Community{id: community_id}, thread, args, user) end diff --git a/lib/groupher_server_web/schema/cms/cms_metrics.ex b/lib/groupher_server_web/schema/cms/cms_metrics.ex index b5928becf..d2dd2687e 100644 --- a/lib/groupher_server_web/schema/cms/cms_metrics.ex +++ b/lib/groupher_server_web/schema/cms/cms_metrics.ex @@ -303,8 +303,55 @@ defmodule GroupherServerWeb.Schema.CMS.Metrics do field(:content_type, :report_content_type) field(:content_id, :id) pagination_args() - # operate_user_id, - # min_case_count, - # max_case_count, + end + + # works-spec + enum :profit_mode do + value(:free) + value(:ad) + value(:freemium) + value(:paid) + end + + enum :working_mode do + value(:fulltime) + value(:parttime) + end + + object :city do + field(:title, :string) + field(:logo, :string) + field(:desc, :string) + field(:link, :string) + end + + object :techstack do + field(:title, :string) + field(:logo, :string) + field(:desc, :string) + + field(:home_link, :string) + field(:community_link, :string) + field(:category, :string) + end + + object :social do + field(:platform, :string) + field(:link, :string) + end + + object :app_store do + field(:platform, :string) + field(:link, :string) + end + + input_object :social_info do + field(:platform, :string) + field(:link, :string) + end + + input_object :app_store_info do + field(:platform, :string) + field(:link, :string) end end diff --git a/lib/groupher_server_web/schema/cms/cms_types.ex b/lib/groupher_server_web/schema/cms/cms_types.ex index 202bc3c33..57588bea8 100644 --- a/lib/groupher_server_web/schema/cms/cms_types.ex +++ b/lib/groupher_server_web/schema/cms/cms_types.ex @@ -91,6 +91,12 @@ defmodule GroupherServerWeb.Schema.CMS.Types do comments_fields() field(:link_addr, :string) + field(:profit_mode, :string) + field(:working_mode, :string) + field(:cities, list_of(:city), resolve: dataloader(CMS, :cities)) + field(:techstacks, list_of(:techstack), resolve: dataloader(CMS, :techstacks)) + field(:social_info, list_of(:social)) + field(:app_store, list_of(:app_store)) timestamp_fields(:article) end diff --git a/lib/groupher_server_web/schema/cms/mutations/works.ex b/lib/groupher_server_web/schema/cms/mutations/works.ex index 544635926..403ec94a4 100644 --- a/lib/groupher_server_web/schema/cms/mutations/works.ex +++ b/lib/groupher_server_web/schema/cms/mutations/works.ex @@ -10,13 +10,23 @@ defmodule GroupherServerWeb.Schema.CMS.Mutations.Works do field :create_works, :works do arg(:title, non_null(:string)) arg(:body, non_null(:string)) + # not for resolver, is for middleware arg(:community_id, non_null(:id)) arg(:thread, :thread, default_value: :works) arg(:article_tags, list_of(:id)) + arg(:techstacks, list_of(:string)) + arg(:cities, list_of(:string)) + + arg(:profit_mode, :profit_mode) + arg(:working_mode, :working_mode) + + arg(:social_info, list_of(:social_info)) + arg(:app_store, list_of(:app_store_info)) + middleware(M.Authorize, :login) middleware(M.PublishThrottle) - resolve(&R.CMS.create_article/3) + resolve(&R.CMS.create_works/3) middleware(M.Statistics.MakeContribute, for: [:user, :community]) end @@ -25,16 +35,23 @@ defmodule GroupherServerWeb.Schema.CMS.Mutations.Works do arg(:id, non_null(:id)) arg(:title, :string) arg(:body, :string) - arg(:digest, :string) arg(:article_tags, list_of(:id)) - # ... + + arg(:techstacks, list_of(:string)) + arg(:cities, list_of(:string)) + + arg(:profit_mode, :profit_mode) + arg(:working_mode, :working_mode) + + arg(:social_info, list_of(:social_info)) + arg(:app_store, list_of(:app_store_info)) middleware(M.Authorize, :login) middleware(M.PassportLoader, source: :works) middleware(M.Passport, claim: "owner;cms->c?->works.edit") - resolve(&R.CMS.update_article/3) + resolve(&R.CMS.update_works/3) end article_react_mutations(:works, [ diff --git a/priv/repo/migrations/20210930071135_add_more_fields_to_works.exs b/priv/repo/migrations/20210930071135_add_more_fields_to_works.exs new file mode 100644 index 000000000..542d75efc --- /dev/null +++ b/priv/repo/migrations/20210930071135_add_more_fields_to_works.exs @@ -0,0 +1,33 @@ +defmodule GroupherServer.Repo.Migrations.AddMoreFieldsToWorks do + use Ecto.Migration + + # - embeds_many social_info + # - home_link + # - profit_mode + # - fulltime / parttime + # - embeds_many city_info + # - embed_many app_store + # - embeds_many teammate + # - embeds_many techstack + + # - community_link + # interview + ## mailstone + + def change do + alter table(:cms_works) do + add(:home_link, :string) + add(:profit_mode, :string) + add(:working_mode, :string) + + add(:social_info, :map) + add(:city_info, :map) + add(:app_store, :map) + add(:teammate, :map) + add(:techstack, :map) + + add(:community_link, :string) + add(:interview_link, :string) + end + end +end diff --git a/priv/repo/migrations/20210930083012_create_works_techstack.exs b/priv/repo/migrations/20210930083012_create_works_techstack.exs new file mode 100644 index 000000000..c61b7a669 --- /dev/null +++ b/priv/repo/migrations/20210930083012_create_works_techstack.exs @@ -0,0 +1,16 @@ +defmodule GroupherServer.Repo.Migrations.CreateWorksTechstack do + use Ecto.Migration + + def change do + create table(:cms_techstacks) do + add(:title, :string) + add(:logo, :string) + add(:desc, :string) + add(:home_link, :string) + add(:community_link, :string) + add(:category, :string) + + timestamps() + end + end +end diff --git a/priv/repo/migrations/20211001022903_create_works_join_techstack.exs b/priv/repo/migrations/20211001022903_create_works_join_techstack.exs new file mode 100644 index 000000000..1ea596eba --- /dev/null +++ b/priv/repo/migrations/20211001022903_create_works_join_techstack.exs @@ -0,0 +1,12 @@ +defmodule GroupherServer.Repo.Migrations.CreateWorksJoinTechstack do + use Ecto.Migration + + def change do + create table(:works_join_techstacks) do + add(:works_id, references(:cms_works, on_delete: :delete_all), null: false) + add(:techstack_id, references(:cms_techstacks, on_delete: :delete_all), null: false) + end + + create(unique_index(:works_join_techstacks, [:works_id, :techstack_id])) + end +end diff --git a/priv/repo/migrations/20211001111732_add_cities.exs b/priv/repo/migrations/20211001111732_add_cities.exs new file mode 100644 index 000000000..069a717ba --- /dev/null +++ b/priv/repo/migrations/20211001111732_add_cities.exs @@ -0,0 +1,14 @@ +defmodule GroupherServer.Repo.Migrations.AddCities do + use Ecto.Migration + + def change do + create table(:cms_cities) do + add(:title, :string) + add(:logo, :string) + add(:desc, :string) + add(:link, :string) + + timestamps() + end + end +end diff --git a/priv/repo/migrations/20211001112647_create_works_join_cityies.exs b/priv/repo/migrations/20211001112647_create_works_join_cityies.exs new file mode 100644 index 000000000..8272508ef --- /dev/null +++ b/priv/repo/migrations/20211001112647_create_works_join_cityies.exs @@ -0,0 +1,12 @@ +defmodule GroupherServer.Repo.Migrations.CreateWorksJoinCityies do + use Ecto.Migration + + def change do + create table(:works_join_cities) do + add(:works_id, references(:cms_works, on_delete: :delete_all), null: false) + add(:city_id, references(:cms_cities, on_delete: :delete_all), null: false) + end + + create(unique_index(:works_join_cities, [:works_id, :city_id])) + end +end diff --git a/priv/repo/migrations/20211001115257_remove_city_info_in_works.exs b/priv/repo/migrations/20211001115257_remove_city_info_in_works.exs new file mode 100644 index 000000000..c981ad145 --- /dev/null +++ b/priv/repo/migrations/20211001115257_remove_city_info_in_works.exs @@ -0,0 +1,9 @@ +defmodule GroupherServer.Repo.Migrations.RemoveCityInfoInWorks do + use Ecto.Migration + + def change do + alter table(:cms_works) do + remove(:city_info) + end + end +end diff --git a/test/groupher_server/cms/articles/works_test.exs b/test/groupher_server/cms/articles/works_test.exs index 6c8862e34..f0bd6ebd4 100644 --- a/test/groupher_server/cms/articles/works_test.exs +++ b/test/groupher_server/cms/articles/works_test.exs @@ -16,14 +16,83 @@ defmodule GroupherServer.Test.Articles.Works do setup do {:ok, user} = db_insert(:user) {:ok, user2} = db_insert(:user) - {:ok, community} = db_insert(:community) + {:ok, community} = db_insert(:community, %{raw: "home"}) works_attrs = mock_attrs(:works, %{community_id: community.id}) {:ok, ~m(user user2 community works_attrs)a} end - describe "[cms workss curd]" do + describe "[cms real works curd]" do + test "create works with full attrs", ~m(user works_attrs)a do + social_info = [ + %{platform: "github", link: "https://github.com/xxx"}, + %{platform: "twitter", link: "https://twitter.com/xxx"} + ] + + app_store = [ + %{platform: "apple", link: "https://apple.com/xxx"}, + %{platform: "google", link: "https://google.com/xxx"}, + %{platform: "others", link: "https://others.com/xxx"} + ] + + attrs = + works_attrs + |> Map.merge(%{ + profit_mode: "love", + working_mode: "fulltime", + techstacks: ["elixir", "React"], + cities: ["chengdu", "xiamen"], + social_info: social_info, + app_store: app_store + }) + + {:ok, works} = CMS.create_works(attrs, user) + + assert works.profit_mode == "love" + assert works.working_mode == "fulltime" + + assert not is_nil(works.social_info) + assert not is_nil(works.app_store) + assert not is_nil(works.cities) + end + + test "create works with minimal attrs", ~m(user works_attrs)a do + attrs = + works_attrs + |> Map.merge(%{ + profit_mode: "love", + working_mode: "fulltime" + }) + + {:ok, works} = CMS.create_works(attrs, user) + + # IO.inspect(works, label: "the attrs") + assert works.profit_mode == "love" + assert works.working_mode == "fulltime" + end + + test "update works with full attrs", ~m(user works_attrs)a do + {:ok, works} = CMS.create_works(works_attrs, user) + + social_info = [ + %{platform: "github", link: "https://github.com/xxx"}, + %{platform: "twitter", link: "https://twitter.com/xxx"} + ] + + app_store = [ + %{platform: "apple", link: "https://apple.com/xxx"}, + %{platform: "google", link: "https://google.com/xxx"}, + %{platform: "others", link: "https://others.com/xxx"} + ] + + {:ok, works} = CMS.update_works(works, %{social_info: social_info, app_store: app_store}) + assert not is_nil(works.social_info) + assert not is_nil(works.app_store) + end + end + + describe "[cms works curd]" do test "can create works with valid attrs", ~m(user community works_attrs)a do assert {:error, _} = ORM.find_by(Author, user_id: user.id) {:ok, works} = CMS.create_article(community, :works, works_attrs, user) diff --git a/test/groupher_server/seeds/articles_seed_test.exs b/test/groupher_server/seeds/articles_seed_test.exs index e0a683722..8b3d54662 100644 --- a/test/groupher_server/seeds/articles_seed_test.exs +++ b/test/groupher_server/seeds/articles_seed_test.exs @@ -11,7 +11,6 @@ defmodule GroupherServer.Test.Seeds.Articles do alias Helper.ORM describe "[posts seed]" do - @tag :wip test "can seed posts" do {:ok, community} = CMS.seed_community(:home) CMS.seed_articles(community, :post, 5) diff --git a/test/groupher_server_web/mutation/cms/articles/works_test.exs b/test/groupher_server_web/mutation/cms/articles/works_test.exs index a63ba0e03..d406042d8 100644 --- a/test/groupher_server_web/mutation/cms/articles/works_test.exs +++ b/test/groupher_server_web/mutation/cms/articles/works_test.exs @@ -1,6 +1,7 @@ defmodule GroupherServer.Test.Mutation.Articles.Works do use GroupherServer.TestTools + import Helper.Utils, only: [keys_to_atoms: 1, camelize_map_key: 1] alias Helper.ORM alias GroupherServer.{CMS, Repo} @@ -8,7 +9,7 @@ defmodule GroupherServer.Test.Mutation.Articles.Works do setup do {:ok, user} = db_insert(:user) - {:ok, community} = db_insert(:community) + {:ok, community} = db_insert(:community, %{raw: "home"}) works_attrs = mock_attrs(:works, %{community_id: community.id}) {:ok, works} = CMS.create_article(community, :works, works_attrs, user) @@ -26,16 +27,48 @@ defmodule GroupherServer.Test.Mutation.Articles.Works do $title: String!, $body: String, $communityId: ID!, + $profitMode: ProfitMode, + $workingMode: WorkingMode, + $cities: [String], + $techstacks: [String], + $socialInfo: [SocialInfo], + $appStore: [AppStoreInfo], $articleTags: [Id] ) { createWorks( title: $title, body: $body, communityId: $communityId, + profitMode: $profitMode, + workingMode: $workingMode, + cities: $cities, + techstacks: $techstacks, + socialInfo: $socialInfo, + appStore: $appStore, articleTags: $articleTags ) { id title + profitMode + workingMode + cities { + title + logo + link + } + techstacks { + title + desc + logo + } + socialInfo { + platform + link + } + appStore { + platform + link + } document { bodyHtml } @@ -49,12 +82,38 @@ defmodule GroupherServer.Test.Mutation.Articles.Works do } } """ - test "create works with valid attrs and make sure author exsit" do + @tag :wip + test "create works with valid attrs and make sure author exsit", ~m(community)a do {:ok, user} = db_insert(:user) user_conn = simu_conn(:user, user) - {:ok, community} = db_insert(:community) - works_attr = mock_attrs(:works) + works_attr = + mock_attrs(:works, %{ + profitMode: "FREE", + workingMode: "FULLTIME", + cities: ["chengdu", "xiamen"], + techstacks: ["elixir", "React"], + socialInfo: [ + %{ + platform: "TWITTER", + link: "https://twitter.com/xxx" + }, + %{ + platform: "GITHUB", + link: "https://github.com/xxx" + } + ], + appStore: [ + %{ + platform: "apple", + link: "https://apple.com/xxx" + }, + %{ + platform: "others", + link: "https://others.com/xxx" + } + ] + }) variables = works_attr |> Map.merge(%{communityId: community.id}) |> camelize_map_key @@ -63,7 +122,13 @@ defmodule GroupherServer.Test.Mutation.Articles.Works do {:ok, found} = ORM.find(Works, created["id"]) assert created["id"] == to_string(found.id) + assert created["profitMode"] == "FREE" + assert created["workingMode"] == "FULLTIME" assert created["originalCommunity"]["id"] == to_string(community.id) + assert not is_nil(created["cities"]) + assert not is_nil(created["techstacks"]) + assert not is_nil(created["socialInfo"]) + assert not is_nil(created["appStore"]) assert created["id"] == to_string(found.id) end @@ -84,12 +149,10 @@ defmodule GroupherServer.Test.Mutation.Articles.Works do assert exist_in?(%{id: article_tag.id}, works.article_tags) end - test "create works should excape xss attracts" do + test "create works should excape xss attracts", ~m(community)a do {:ok, user} = db_insert(:user) user_conn = simu_conn(:user, user) - {:ok, community} = db_insert(:community) - works_attr = mock_attrs(:works, %{body: mock_xss_string()}) variables = works_attr |> Map.merge(%{communityId: community.id}) |> camelize_map_key result = user_conn |> mutation_result(@create_works_query, variables, "createWorks") @@ -100,12 +163,10 @@ defmodule GroupherServer.Test.Mutation.Articles.Works do assert not String.contains?(body_html, "script") end - test "create works should excape xss attracts 2" do + test "create works should excape xss attracts 2", ~m(community)a do {:ok, user} = db_insert(:user) user_conn = simu_conn(:user, user) - {:ok, community} = db_insert(:community) - works_attr = mock_attrs(:works, %{body: mock_xss_string(:safe)}) variables = works_attr |> Map.merge(%{communityId: community.id}) |> camelize_map_key result = user_conn |> mutation_result(@create_works_query, variables, "createWorks") @@ -116,10 +177,52 @@ defmodule GroupherServer.Test.Mutation.Articles.Works do end @query """ - mutation($id: ID!, $title: String, $body: String, $articleTags: [Ids]){ - updateWorks(id: $id, title: $title, body: $body, articleTags: $articleTags) { + mutation( + $id: ID!, + $title: String, + $body: String, + $profitMode: ProfitMode, + $workingMode: WorkingMode, + $cities: [String], + $techstacks: [String], + $socialInfo: [SocialInfo], + $appStore: [AppStoreInfo], + $articleTags: [Ids] + ){ + updateWorks( + id: $id, + title: $title, + body: $body, + profitMode: $profitMode, + workingMode: $workingMode, + cities: $cities, + techstacks: $techstacks, + socialInfo: $socialInfo, + appStore: $appStore, + articleTags: $articleTags + ) { id title + profitMode + workingMode + cities { + title + logo + link + } + techstacks { + title + desc + logo + } + socialInfo { + platform + link + } + appStore { + platform + link + } document { bodyHtml } @@ -129,19 +232,54 @@ defmodule GroupherServer.Test.Mutation.Articles.Works do } } """ - test "update a works without login user fails", ~m(guest_conn works)a do + @tag :wip + test "works can be update by owner", ~m(owner_conn works)a do unique_num = System.unique_integer([:positive, :monotonic]) variables = %{ id: works.id, title: "updated title #{unique_num}", - body: mock_rich_text("updated body #{unique_num}") + body: mock_rich_text("updated body #{unique_num}"), + profitMode: "FREE", + workingMode: "FULLTIME", + cities: ["chengdu", "xiamen"], + techstacks: ["elixir", "React"], + socialInfo: [ + %{ + platform: "TWITTER", + link: "https://twitter.com/xxx" + }, + %{ + platform: "GITHUB", + link: "https://github.com/xxx" + } + ], + appStore: [ + %{ + platform: "apple", + link: "https://apple.com/xxx" + }, + %{ + platform: "others", + link: "https://others.com/xxx" + } + ] } - assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + result = owner_conn |> mutation_result(@query, variables, "updateWorks") + + assert result["title"] == variables.title + assert result["appStore"] |> Enum.map(&keys_to_atoms(&1)) == variables.appStore + assert result["socialInfo"] |> Enum.map(&keys_to_atoms(&1)) == variables.socialInfo + assert result["profitMode"] == variables.profitMode + assert result["workingMode"] == variables.workingMode + + assert result + |> get_in(["document", "bodyHtml"]) + |> String.contains?(~s(updated body #{unique_num})) end - test "works can be update by owner", ~m(owner_conn works)a do + test "update a works without login user fails", ~m(guest_conn works)a do unique_num = System.unique_integer([:positive, :monotonic]) variables = %{ @@ -150,13 +288,7 @@ defmodule GroupherServer.Test.Mutation.Articles.Works do body: mock_rich_text("updated body #{unique_num}") } - result = owner_conn |> mutation_result(@query, variables, "updateWorks") - - assert result["title"] == variables.title - - assert result - |> get_in(["document", "bodyHtml"]) - |> String.contains?(~s(updated body #{unique_num})) + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) end test "login user with auth passport update a works", ~m(works)a do