From 9af466790af3ee7115b32966dbfd4f3f254690a0 Mon Sep 17 00:00:00 2001 From: dtluna Date: Mon, 15 May 2017 00:19:13 +0300 Subject: [PATCH] Refactored status-related Twitter API --- lib/pleroma/misc.ex | 6 + lib/pleroma/user.ex | 25 +++ lib/pleroma/web/router.ex | 30 ++-- .../controllers/status_controller.ex | 150 +++++++++++++++++ .../controllers/user_controller.ex | 96 +++++------ .../representers/activity_representer.ex | 138 --------------- .../representers/base_representer.ex | 28 ---- .../representers/object_representer.ex | 20 --- .../representers/user_representer.ex | 37 ----- lib/pleroma/web/twitter_api/twitter_api.ex | 144 ---------------- .../web/twitter_api/twitter_api_controller.ex | 157 ------------------ .../web/twitter_api/views/attachment_view.ex | 14 ++ .../web/twitter_api/views/status_view.ex | 155 +++++++++++++++++ .../web/twitter_api/views/user_view.ex | 33 ++-- test/support/builders/activity_builder.ex | 3 +- test/support/factory.ex | 8 +- .../activity_representer_test.exs | 131 --------------- .../representers/object_representer_test.exs | 31 ---- .../representers/user_representer_test.exs | 82 --------- .../twitter_api_controller_test.exs | 36 ++-- test/web/twitter_api/twitter_api_test.exs | 156 +---------------- 21 files changed, 453 insertions(+), 1027 deletions(-) create mode 100644 lib/pleroma/web/twitter_api/controllers/status_controller.ex delete mode 100644 lib/pleroma/web/twitter_api/representers/activity_representer.ex delete mode 100644 lib/pleroma/web/twitter_api/representers/base_representer.ex delete mode 100644 lib/pleroma/web/twitter_api/representers/object_representer.ex delete mode 100644 lib/pleroma/web/twitter_api/representers/user_representer.ex create mode 100644 lib/pleroma/web/twitter_api/views/attachment_view.ex delete mode 100644 test/web/twitter_api/representers/activity_representer_test.exs delete mode 100644 test/web/twitter_api/representers/object_representer_test.exs delete mode 100644 test/web/twitter_api/representers/user_representer_test.exs diff --git a/lib/pleroma/misc.ex b/lib/pleroma/misc.ex index 80c375735..d238d9732 100644 --- a/lib/pleroma/misc.ex +++ b/lib/pleroma/misc.ex @@ -2,4 +2,10 @@ defmodule Pleroma.Misc do def make_date do DateTime.utc_now() |> DateTime.to_iso8601 end + + def to_boolean(false), do: false + + def to_boolean(nil), do: false + + def to_boolean(_), do: true end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 22e77c3df..696adb45b 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -166,4 +166,29 @@ defmodule Pleroma.User do end end end + + def get_by_params(user \\ nil, params) do + case params do + %{"user_id" => user_id} -> + case target = Repo.get(User, user_id) do + nil -> + {:not_found, "No user with such user_id"} + _ -> + {:ok, target} + end + %{"screen_name" => nickname} -> + case target = Repo.get_by(User, nickname: nickname) do + nil -> + {:not_found, "No user with such screen_name"} + _ -> + {:ok, target} + end + _ -> + if user do + {:ok, user} + else + {:bad_request, "You need to specify screen_name or user_id"} + end + end + end end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 5717951dc..76e0f1aff 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -29,13 +29,13 @@ defmodule Pleroma.Web.Router do get "/help/test", TwitterAPI.UtilController, :help_test get "/statusnet/config", TwitterAPI.UtilController, :config - get "/statuses/public_timeline", TwitterAPI.Controller, :public_timeline - get "/statuses/public_and_external_timeline", TwitterAPI.Controller, :public_and_external_timeline - get "/statuses/networkpublic_timeline", TwitterAPI.Controller, :public_and_external_timeline - get "/statuses/user_timeline", TwitterAPI.Controller, :user_timeline + get "/statuses/public_timeline", TwitterAPI.StatusController, :public_timeline + get "/statuses/public_and_external_timeline", TwitterAPI.StatusController, :public_and_external_timeline + get "/statuses/networkpublic_timeline", TwitterAPI.StatusController, :public_and_external_timeline + get "/statuses/user_timeline", TwitterAPI.StatusController, :user_timeline - get "/statuses/show/:id", TwitterAPI.Controller, :fetch_status - get "/statusnet/conversation/:id", TwitterAPI.Controller, :fetch_conversation + get "/statuses/show/:id", TwitterAPI.StatusController, :fetch_status + get "/statusnet/conversation/:id", TwitterAPI.StatusController, :fetch_conversation post "/account/register", TwitterAPI.UserController, :register end @@ -46,13 +46,13 @@ defmodule Pleroma.Web.Router do get "/account/verify_credentials", TwitterAPI.UserController, :verify_credentials post "/account/verify_credentials", TwitterAPI.UserController, :verify_credentials - get "/statuses/home_timeline", TwitterAPI.Controller, :friends_timeline - get "/statuses/friends_timeline", TwitterAPI.Controller, :friends_timeline - get "/statuses/mentions", TwitterAPI.Controller, :mentions_timeline - get "/statuses/mentions_timeline", TwitterAPI.Controller, :mentions_timeline + get "/statuses/home_timeline", TwitterAPI.StatusController, :friends_timeline + get "/statuses/friends_timeline", TwitterAPI.StatusController, :friends_timeline + get "/statuses/mentions", TwitterAPI.StatusController, :mentions_timeline + get "/statuses/mentions_timeline", TwitterAPI.StatusController, :mentions_timeline - post "/statuses/update", TwitterAPI.Controller, :status_update - post "/statuses/retweet/:id", TwitterAPI.Controller, :retweet + post "/statuses/update", TwitterAPI.StatusController, :status_update + post "/statuses/retweet/:id", TwitterAPI.StatusController, :retweet post "/friendships/create", TwitterAPI.UserController, :follow post "/friendships/destroy", TwitterAPI.UserController, :unfollow @@ -60,9 +60,9 @@ defmodule Pleroma.Web.Router do post "/statusnet/media/upload", TwitterAPI.Controller, :upload post "/media/upload", TwitterAPI.Controller, :upload_json - post "/favorites/create/:id", TwitterAPI.Controller, :favorite - post "/favorites/create", TwitterAPI.Controller, :favorite - post "/favorites/destroy/:id", TwitterAPI.Controller, :unfavorite + post "/favorites/create/:id", TwitterAPI.StatusController, :favorite + post "/favorites/create", TwitterAPI.StatusController, :favorite + post "/favorites/destroy/:id", TwitterAPI.StatusController, :unfavorite post "/qvitter/update_avatar", TwitterAPI.UserController, :update_avatar end diff --git a/lib/pleroma/web/twitter_api/controllers/status_controller.ex b/lib/pleroma/web/twitter_api/controllers/status_controller.ex new file mode 100644 index 000000000..b3ca55da4 --- /dev/null +++ b/lib/pleroma/web/twitter_api/controllers/status_controller.ex @@ -0,0 +1,150 @@ +defmodule Pleroma.Web.TwitterAPI.StatusController do + use Pleroma.Web, :controller + alias Pleroma.{Activity, Repo, Object, User, Misc} + alias Pleroma.Web.TwitterAPI.{ErrorView, TwitterAPI, UserController} + alias Pleroma.Web.ActivityPub.ActivityPub + + def status_update(%{assigns: %{user: user}} = conn, %{"status" => status_text} = status_data) do + l = status_text |> String.trim |> String.length + if l > 0 && l < 5000 do + media_ids = extract_media_ids(status_data) + {:ok, activity} = TwitterAPI.create_status(user, Map.put(status_data, "media_ids", media_ids)) + render(conn, "show.json", %{activity: activity, for: user}) + else + empty_status_reply(conn) + end + end + + def status_update(conn, _status_data) do + empty_status_reply(conn) + end + + defp empty_status_reply(conn) do + message = "Client must provide a 'status' parameter with a value." + conn + |> put_status(:bad_request) + |> render(ErrorView, "error.json", %{request_path: conn.request_path, message: message}) + end + + defp extract_media_ids(status_data) do + with media_ids when not is_nil(media_ids) <- status_data["media_ids"], + split_ids <- String.split(media_ids, ","), + clean_ids <- Enum.reject(split_ids, fn (id) -> String.length(id) == 0 end) + do + clean_ids + else _e -> [] + end + end + + def public_and_external_timeline(%{assigns: %{user: user}} = conn, params) do + activities = ActivityPub.fetch_public_activities(params) + render(conn, "timeline.json", %{activities: activities, for: user}) + end + + def public_timeline(%{assigns: %{user: user}} = conn, params) do + activities = ActivityPub.fetch_public_activities(Map.put(params, "local_only", true)) + render(conn, "timeline.json", %{activities: activities, for: user}) + end + + def friends_timeline(%{assigns: %{user: user}} = conn, params) do + activities = ActivityPub.fetch_activities([user.ap_id | user.following], params) + render(conn, "timeline.json", %{activities: activities, for: user}) + end + + def user_timeline(conn, params) do + case UserController.find_user(conn, params) do + {:error, response} -> response + + {:ok, user = %User{ap_id: ap_id}} -> + activities = ActivityPub.fetch_activities([], Map.merge(params, %{"actor_id" => ap_id})) + render(conn, "timeline.json", %{activities: activities, for: user}) + end + end + + def mentions_timeline(%{assigns: %{user: user}} = conn, params) do + activities = ActivityPub.fetch_activities([user.ap_id], params) + render(conn, "timeline.json", %{activities: activities, for: user}) + end + + def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do + case find_activity(conn, id) do + {:not_found, response} -> response + {:ok, activity = %Activity{}} -> render(conn, "show.json", %{activity: activity, for: user}) + end + end + + def fetch_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do + id = String.to_integer(id) + with context when is_binary(context) <- TwitterAPI.conversation_id_to_context(id), + activities <- ActivityPub.fetch_activities_for_context(context) + do + render(conn, "timeline.json", %{activities: activities, for: user}) + else _e -> + json(conn, []) + end + end + + def favorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do + case find_activity(conn, id) do + {:not_found, response} -> response + {:ok, activity = %Activity{data: %{"object" => %{"id" => object_id}}}} -> + object = Object.get_by_ap_id(object_id) + case ActivityPub.like(user, object) do + {:ok, _like, object = %Object{}} -> + activity = update_data(activity, object) + render(conn, "show.json", %{activity: activity, for: user}) + end + end + end + + def unfavorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do + case find_activity(conn, id) do + {:not_found, response} -> response + {:ok, activity = %Activity{data: %{"object" => %{"id" => object_id}}}} -> + object = Object.get_by_ap_id(object_id) + case ActivityPub.unlike(user, object) do + {:ok, object = %Object{}} -> + activity = update_data(activity, object) + render(conn, "show.json", %{activity: activity, for: user}) + end + end + end + + def retweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do + case find_activity(conn, id) do + {:not_found, response} -> response + {:ok, activity = %Activity{data: %{"actor" => actor, "object" => %{"id" => object_id}}}} -> + if actor == user.ap_id do + conn + |> put_status(:bad_request) + |> render(ErrorView, "error.json", %{request_path: conn.request_path, + message: "You cannot repeat your own status."}) + else + object = Object.get_by_ap_id(object_id) + case ActivityPub.announce(user, object) do + {:ok, _announce, object = %Object{}} -> + activity = update_data(activity, object) + render(conn, "show.json", %{activity: activity, for: user}) + end + end + end + end + + defp update_data(activity = %Activity{data: data}, %Object{data: object_data}) do + new_data = Map.put(data, "object", object_data) + %{activity | data: new_data} + end + + defp find_activity(conn, id) do + case Repo.get(Activity, id) do + nil -> + response = conn + |> put_status(:not_found) + |> render(ErrorView, "error.json", %{request_path: conn.request_path, + message: "No such status."}) + {:not_found, response} + + activity = %Activity{} -> {:ok, activity} + end + end +end diff --git a/lib/pleroma/web/twitter_api/controllers/user_controller.ex b/lib/pleroma/web/twitter_api/controllers/user_controller.ex index 11b6932b4..7dc7ed1de 100644 --- a/lib/pleroma/web/twitter_api/controllers/user_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/user_controller.ex @@ -1,45 +1,48 @@ defmodule Pleroma.Web.TwitterAPI.UserController do use Pleroma.Web, :controller alias Ecto.Changeset - alias Pleroma.{Repo, User, Misc} + alias Pleroma.{Activity, Object, Repo, User, Misc} alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.TwitterAPI.ErrorView def verify_credentials(%{assigns: %{user: user}} = conn, _params) do render conn, "show.json", %{user: user, for: user} end def follow(%{assigns: %{user: follower}} = conn, params) do - with {:ok, %User{} = followed} <- get_user(params), - {:ok, follower} <- User.follow(follower, followed), - {:ok, activity} <- ActivityPub.follow(follower, followed) - do - render conn, "show.json", %{user: followed, for: follower} - else - {:error, message} -> - conn - |> put_status(:not_found) - |> render(Pleroma.Web.TwitterAPI.ErrorView, "error.json", - %{request_path: conn.request_path, message: message}) + case find_user(conn, params) do + {:ok, followed = %User{}} -> + case User.follow(follower, followed) do + {:ok, follower = %User{}} -> + {:ok, _activity} = ActivityPub.follow(follower, followed) + render conn, "show.json", %{user: followed, for: follower} + {:error, message} -> + conn + |> put_status(:bad_request) + |> render(ErrorView, "error.json", %{request_path: conn.request_path, message: message}) + end + {:error, response} -> response end end def unfollow(%{assigns: %{user: follower}} = conn, params) do - with { :ok, %User{} = unfollowed } <- get_user(params), - { :ok, follower, follow_activity } <- User.unfollow(follower, unfollowed), - { :ok, _activity } <- ActivityPub.insert(%{ - "type" => "Undo", - "actor" => follower.ap_id, - "object" => follow_activity.data["id"], # get latest Follow for these users - "published" => Misc.make_date() - }) - do - render conn, "show.json", %{user: unfollowed, for: follower} - else - {:error, message} -> - conn - |> put_status(:not_found) - |> render(Pleroma.Web.TwitterAPI.ErrorView, "error.json", - %{request_path: conn.request_path, message: message}) + case find_user(conn, params) do + {:ok, unfollowed = %User{ap_id: unfollowed_id}} -> + case User.unfollow(follower, unfollowed) do + {:ok, follower = %User{ap_id: ap_id}, %Activity{data: %{"id" => id}}} -> + {:ok, _activity} = ActivityPub.insert(%{ + "type" => "Undo", + "actor" => ap_id, + "object" => id, # get latest Follow for these users + "published" => Misc.make_date() + }) + render conn, "show.json", %{user: unfollowed, for: follower} + {:error, message} -> + conn + |> put_status(:bad_request) + |> render(ErrorView, "error.json", %{request_path: conn.request_path, message: message}) + end + {:error, response} -> response end end @@ -62,41 +65,26 @@ defmodule Pleroma.Web.TwitterAPI.UserController do errors = Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end) conn |> put_status(:bad_request) - |> render(Pleroma.Web.TwitterAPI.ErrorView, "error.json", + |> render(ErrorView, "error.json", %{request_path: conn.request_path, message: errors}) end end def update_avatar(%{assigns: %{user: user}} = conn, params) do - {:ok, object} = ActivityPub.upload(params) - change = Changeset.change(user, %{avatar: object.data}) + {:ok, %Object{data: data}} = ActivityPub.upload(params) + change = Changeset.change(user, %{avatar: data}) {:ok, user} = Repo.update(change) - render conn, "show.json", %{user: user} end - defp get_user(user \\ nil, params) do - case params do - %{"user_id" => user_id} -> - case target = Repo.get(User, user_id) do - nil -> - {:error, "No user with such user_id"} - _ -> - {:ok, target} - end - %{"screen_name" => nickname} -> - case target = Repo.get_by(User, nickname: nickname) do - nil -> - {:error, "No user with such screen_name"} - _ -> - {:ok, target} - end - _ -> - if user do - {:ok, user} - else - {:error, "You need to specify screen_name or user_id"} - end + def find_user(%{assigns: %{user: user}} = conn, params) do + case User.get_by_params(user, params) do + {:ok, user = %User{}} -> {:ok, user} + {status, message} -> + response = conn + |> put_status(status) + |> render(ErrorView, "error.json", %{request_path: conn.request_path, message: message}) + {:error, response} end end end diff --git a/lib/pleroma/web/twitter_api/representers/activity_representer.ex b/lib/pleroma/web/twitter_api/representers/activity_representer.ex deleted file mode 100644 index 7266a0b2d..000000000 --- a/lib/pleroma/web/twitter_api/representers/activity_representer.ex +++ /dev/null @@ -1,138 +0,0 @@ -defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do - use Pleroma.Web.TwitterAPI.Representers.BaseRepresenter - alias Pleroma.Web.TwitterAPI.Representers.{UserRepresenter, ObjectRepresenter} - alias Pleroma.{Activity, User} - alias Calendar.Strftime - alias Pleroma.Web.TwitterAPI.TwitterAPI - - defp user_by_ap_id(user_list, ap_id) do - Enum.find(user_list, fn (%{ap_id: user_id}) -> ap_id == user_id end) - end - - def to_map(%Activity{data: %{"type" => "Announce", "actor" => actor, "published" => created_at}} = activity, - %{users: users, announced_activity: announced_activity} = opts) do - user = user_by_ap_id(users, actor) - created_at = created_at |> date_to_asctime - - text = "#{user.nickname} retweeted a status." - - announced_user = user_by_ap_id(users, announced_activity.data["actor"]) - retweeted_status = to_map(announced_activity, Map.merge(%{user: announced_user}, opts)) - %{ - "id" => activity.id, - "user" => UserRepresenter.to_map(user, opts), - "statusnet_html" => text, - "text" => text, - "is_local" => true, - "is_post_verb" => false, - "uri" => "tag:#{activity.data["id"]}:objectType=note", - "created_at" => created_at, - "retweeted_status" => retweeted_status, - "statusnet_conversation_id" => conversation_id(announced_activity), - "external_url" => activity.data["id"] - } - end - - def to_map(%Activity{data: %{"type" => "Like", "published" => created_at}} = activity, - %{user: user, liked_activity: liked_activity} = opts) do - created_at = created_at |> date_to_asctime - - text = "#{user.nickname} favorited a status." - - %{ - "id" => activity.id, - "user" => UserRepresenter.to_map(user, opts), - "statusnet_html" => text, - "text" => text, - "is_local" => true, - "is_post_verb" => false, - "uri" => "tag:#{activity.data["id"]}:objectType=Favourite", - "created_at" => created_at, - "in_reply_to_status_id" => liked_activity.id, - "external_url" => activity.data["id"] - } - end - - def to_map(%Activity{data: %{"type" => "Follow", "published" => created_at, "object" => followed_id}} = activity, %{user: user} = opts) do - created_at = created_at |> date_to_asctime - - followed = User.get_cached_by_ap_id(followed_id) - text = "#{user.nickname} started following #{followed.nickname}" - %{ - "id" => activity.id, - "user" => UserRepresenter.to_map(user, opts), - "attentions" => [], - "statusnet_html" => text, - "text" => text, - "is_local" => true, - "is_post_verb" => false, - "created_at" => created_at, - "in_reply_to_status_id" => nil, - "external_url" => activity.data["id"] - } - end - - def to_map(%Activity{data: %{"object" => %{"content" => content} = object}} = activity, %{user: user} = opts) do - created_at = object["published"] |> date_to_asctime - like_count = object["like_count"] || 0 - announcement_count = object["announcement_count"] || 0 - favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || []) - repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || []) - - mentions = opts[:mentioned] || [] - - attentions = activity.data["to"] - |> Enum.map(fn (ap_id) -> Enum.find(mentions, fn(user) -> ap_id == user.ap_id end) end) - |> Enum.filter(&(&1)) - |> Enum.map(fn (user) -> UserRepresenter.to_map(user, opts) end) - - conversation_id = conversation_id(activity) - - %{ - "id" => activity.id, - "user" => UserRepresenter.to_map(user, opts), - "attentions" => [], - "statusnet_html" => content, - "text" => HtmlSanitizeEx.strip_tags(content), - "is_local" => true, - "is_post_verb" => true, - "created_at" => created_at, - "in_reply_to_status_id" => object["inReplyToStatusId"], - "statusnet_conversation_id" => conversation_id, - "attachments" => (object["attachment"] || []) |> ObjectRepresenter.enum_to_list(opts), - "attentions" => attentions, - "fave_num" => like_count, - "repeat_num" => announcement_count, - "favorited" => to_boolean(favorited), - "repeated" => to_boolean(repeated), - "external_url" => activity.data["id"] - } - end - - def conversation_id(activity) do - with context when not is_nil(context) <- activity.data["context"] do - TwitterAPI.context_to_conversation_id(context) - else _e -> nil - end - end - - defp date_to_asctime(date) do - with {:ok, date, _offset} <- date |> DateTime.from_iso8601 do - Strftime.strftime!(date, "%a %b %d %H:%M:%S %z %Y") - else _e -> - "" - end - end - - defp to_boolean(false) do - false - end - - defp to_boolean(nil) do - false - end - - defp to_boolean(_) do - true - end -end diff --git a/lib/pleroma/web/twitter_api/representers/base_representer.ex b/lib/pleroma/web/twitter_api/representers/base_representer.ex deleted file mode 100644 index a4ef245fc..000000000 --- a/lib/pleroma/web/twitter_api/representers/base_representer.ex +++ /dev/null @@ -1,28 +0,0 @@ -defmodule Pleroma.Web.TwitterAPI.Representers.BaseRepresenter do - defmacro __using__(_opts) do - quote do - def to_json(object) do to_json(object, %{}) end - def to_json(object, options) do - object - |> to_map(options) - |> Poison.encode! - end - - def enum_to_list(enum, options) do - mapping = fn (el) -> to_map(el, options) end - Enum.map(enum, mapping) - end - - def to_map(object) do - to_map(object, %{}) - end - - def enum_to_json(enum) do enum_to_json(enum, %{}) end - def enum_to_json(enum, options) do - enum - |> enum_to_list(options) - |> Poison.encode! - end - end - end -end diff --git a/lib/pleroma/web/twitter_api/representers/object_representer.ex b/lib/pleroma/web/twitter_api/representers/object_representer.ex deleted file mode 100644 index c39b60760..000000000 --- a/lib/pleroma/web/twitter_api/representers/object_representer.ex +++ /dev/null @@ -1,20 +0,0 @@ -defmodule Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter do - use Pleroma.Web.TwitterAPI.Representers.BaseRepresenter - alias Pleroma.Object - - def to_map(%Object{} = object, _opts) do - data = object.data - url = List.first(data["url"]) - %{ - url: url["href"], - mimetype: url["mediaType"], - id: data["uuid"], - oembed: false - } - end - - # If we only get the naked data, wrap in an object - def to_map(%{} = data, opts) do - to_map(%Object{data: data}, opts) - end -end diff --git a/lib/pleroma/web/twitter_api/representers/user_representer.ex b/lib/pleroma/web/twitter_api/representers/user_representer.ex deleted file mode 100644 index 493077413..000000000 --- a/lib/pleroma/web/twitter_api/representers/user_representer.ex +++ /dev/null @@ -1,37 +0,0 @@ -defmodule Pleroma.Web.TwitterAPI.Representers.UserRepresenter do - use Pleroma.Web.TwitterAPI.Representers.BaseRepresenter - - alias Pleroma.User - - def to_map(user, opts) do - image = User.avatar_url(user) - following = if opts[:for] do - User.following?(opts[:for], user) - else - false - end - - user_info = User.get_cached_user_info(user) - - map = %{ - "id" => user.id, - "name" => user.name, - "screen_name" => user.nickname, - "description" => user.bio, - "following" => following, - # Fake fields - "favourites_count" => 0, - "statuses_count" => user_info[:note_count], - "friends_count" => user_info[:following_count], - "followers_count" => user_info[:follower_count], - "profile_image_url" => image, - "profile_image_url_https" => image, - "profile_image_url_profile_size" => image, - "profile_image_url_original" => image, - "rights" => %{}, - "statusnet_profile_url" => user.ap_id - } - - map - end -end diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index 872e605b6..d616bcd21 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -90,88 +90,6 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do ActivityPub.create(to, user, context, object, additional, data) end - def fetch_friend_statuses(user, opts \\ %{}) do - ActivityPub.fetch_activities([user.ap_id | user.following], opts) - |> activities_to_statuses(%{for: user}) - end - - def fetch_public_statuses(user, opts \\ %{}) do - opts = Map.put(opts, "local_only", true) - ActivityPub.fetch_public_activities(opts) - |> activities_to_statuses(%{for: user}) - end - - def fetch_public_and_external_statuses(user, opts \\ %{}) do - ActivityPub.fetch_public_activities(opts) - |> activities_to_statuses(%{for: user}) - end - - def fetch_user_statuses(user, opts \\ %{}) do - ActivityPub.fetch_activities([], opts) - |> activities_to_statuses(%{for: user}) - end - - def fetch_mentions(user, opts \\ %{}) do - ActivityPub.fetch_activities([user.ap_id], opts) - |> activities_to_statuses(%{for: user}) - end - - def fetch_conversation(user, id) do - with context when is_binary(context) <- conversation_id_to_context(id), - activities <- ActivityPub.fetch_activities_for_context(context), - statuses <- activities |> activities_to_statuses(%{for: user}) - do - statuses - else _e -> - [] - end - end - - def fetch_status(user, id) do - with %Activity{} = activity <- Repo.get(Activity, id) do - activity_to_status(activity, %{for: user}) - end - end - - def favorite(%User{} = user, %Activity{data: %{"object" => object}} = activity) do - object = Object.get_by_ap_id(object["id"]) - - {:ok, _like_activity, object} = ActivityPub.like(user, object) - new_data = activity.data - |> Map.put("object", object.data) - - status = %{activity | data: new_data} - |> activity_to_status(%{for: user}) - - {:ok, status} - end - - def unfavorite(%User{} = user, %Activity{data: %{"object" => object}} = activity) do - object = Object.get_by_ap_id(object["id"]) - - {:ok, object} = ActivityPub.unlike(user, object) - new_data = activity.data - |> Map.put("object", object.data) - - status = %{activity | data: new_data} - |> activity_to_status(%{for: user}) - - {:ok, status} - end - - def retweet(%User{} = user, %Activity{data: %{"object" => object}} = activity) do - object = Object.get_by_ap_id(object["id"]) - - {:ok, _announce_activity, object} = ActivityPub.announce(user, object) - new_data = activity.data - |> Map.put("object", object.data) - - status = %{activity | data: new_data} - |> activity_to_status(%{for: user}) - - {:ok, status} - end - def upload(%Plug.Upload{} = file, format \\ "xml") do {:ok, object} = ActivityPub.upload(file) @@ -218,68 +136,6 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do Enum.reduce(mentions, text, fn ({match, %User{ap_id: ap_id}}, text) -> String.replace(text, match, "#{match}") end) end - def get_user(user \\ nil, params) do - case params do - %{"user_id" => user_id} -> - case target = Repo.get(User, user_id) do - nil -> - {:error, "No user with such user_id"} - _ -> - {:ok, target} - end - %{"screen_name" => nickname} -> - case target = Repo.get_by(User, nickname: nickname) do - nil -> - {:error, "No user with such screen_name"} - _ -> - {:ok, target} - end - _ -> - if user do - {:ok, user} - else - {:error, "You need to specify screen_name or user_id"} - end - end - end - - defp activities_to_statuses(activities, opts) do - Enum.map(activities, fn(activity) -> - activity_to_status(activity, opts) - end) - end - - # For likes, fetch the liked activity, too. - defp activity_to_status(%Activity{data: %{"type" => "Like"}} = activity, opts) do - actor = get_in(activity.data, ["actor"]) - user = User.get_cached_by_ap_id(actor) - [liked_activity] = Activity.all_by_object_ap_id(activity.data["object"]) - - ActivityRepresenter.to_map(activity, Map.merge(opts, %{user: user, liked_activity: liked_activity})) - end - - # For announces, fetch the announced activity and the user. - defp activity_to_status(%Activity{data: %{"type" => "Announce"}} = activity, opts) do - actor = get_in(activity.data, ["actor"]) - user = User.get_cached_by_ap_id(actor) - [announced_activity] = Activity.all_by_object_ap_id(activity.data["object"]) - announced_actor = User.get_cached_by_ap_id(announced_activity.data["actor"]) - - ActivityRepresenter.to_map(activity, Map.merge(opts, %{users: [user, announced_actor], announced_activity: announced_activity})) - end - - defp activity_to_status(activity, opts) do - actor = get_in(activity.data, ["actor"]) - user = User.get_cached_by_ap_id(actor) - # mentioned_users = Repo.all(from user in User, where: user.ap_id in ^activity.data["to"]) - mentioned_users = Enum.map(activity.data["to"] || [], fn (ap_id) -> - User.get_cached_by_ap_id(ap_id) - end) - |> Enum.filter(&(&1)) - - ActivityRepresenter.to_map(activity, Map.merge(opts, %{user: user, mentioned: mentioned_users})) - end - def context_to_conversation_id(context) do with %Object{id: id} <- Object.get_cached_by_ap_id(context) do id diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 3d6ec3b84..b64a0ad3f 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -1,99 +1,6 @@ defmodule Pleroma.Web.TwitterAPI.Controller do use Pleroma.Web, :controller alias Pleroma.Web.TwitterAPI.TwitterAPI - alias Pleroma.Web.TwitterAPI.Representers.{UserRepresenter, ActivityRepresenter} - alias Pleroma.{Web, Repo, Activity} - alias Pleroma.Web.ActivityPub.ActivityPub - alias Ecto.Changeset - - def status_update(%{assigns: %{user: user}} = conn, %{"status" => status_text} = status_data) do - l = status_text |> String.trim |> String.length - if l > 0 && l < 5000 do - media_ids = extract_media_ids(status_data) - {:ok, activity} = TwitterAPI.create_status(user, Map.put(status_data, "media_ids", media_ids)) - conn - |> json_reply(200, ActivityRepresenter.to_json(activity, %{user: user})) - else - empty_status_reply(conn) - end - end - - def status_update(conn, _status_data) do - empty_status_reply(conn) - end - - defp empty_status_reply(conn) do - bad_request_reply(conn, "Client must provide a 'status' parameter with a value.") - end - - defp extract_media_ids(status_data) do - with media_ids when not is_nil(media_ids) <- status_data["media_ids"], - split_ids <- String.split(media_ids, ","), - clean_ids <- Enum.reject(split_ids, fn (id) -> String.length(id) == 0 end) - do - clean_ids - else _e -> [] - end - end - - def public_and_external_timeline(%{assigns: %{user: user}} = conn, params) do - statuses = TwitterAPI.fetch_public_and_external_statuses(user, params) - {:ok, json} = Poison.encode(statuses) - - conn - |> json_reply(200, json) - end - - def public_timeline(%{assigns: %{user: user}} = conn, params) do - statuses = TwitterAPI.fetch_public_statuses(user, params) - {:ok, json} = Poison.encode(statuses) - - conn - |> json_reply(200, json) - end - - def friends_timeline(%{assigns: %{user: user}} = conn, params) do - statuses = TwitterAPI.fetch_friend_statuses(user, params) - {:ok, json} = Poison.encode(statuses) - - conn - |> json_reply(200, json) - end - - def user_timeline(%{assigns: %{user: user}} = conn, params) do - case TwitterAPI.get_user(user, params) do - {:ok, target_user} -> - params = Map.merge(params, %{"actor_id" => target_user.ap_id}) - statuses = TwitterAPI.fetch_user_statuses(user, params) - conn - |> json_reply(200, statuses |> Poison.encode!) - {:error, msg} -> - bad_request_reply(conn, msg) - end - end - - def mentions_timeline(%{assigns: %{user: user}} = conn, params) do - statuses = TwitterAPI.fetch_mentions(user, params) - {:ok, json} = Poison.encode(statuses) - - conn - |> json_reply(200, json) - end - - def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do - response = Poison.encode!(TwitterAPI.fetch_status(user, id)) - - conn - |> json_reply(200, response) - end - - def fetch_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do - id = String.to_integer(id) - response = Poison.encode!(TwitterAPI.fetch_conversation(user, id)) - - conn - |> json_reply(200, response) - end def upload(conn, %{"media" => media}) do response = TwitterAPI.upload(media) @@ -102,79 +9,15 @@ defmodule Pleroma.Web.TwitterAPI.Controller do |> send_resp(200, response) end - def help_test(conn, _params) do - conn |> json_reply(200, Poison.encode!("ok")) - end - def upload_json(conn, %{"media" => media}) do response = TwitterAPI.upload(media, "json") conn |> json_reply(200, response) end - def config(conn, _params) do - response = %{ - site: %{ - name: Web.base_url, - server: Web.base_url, - textlimit: -1 - } - } - |> Poison.encode! - - conn - |> json_reply(200, response) - end - - def favorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do - activity = Repo.get(Activity, id) - {:ok, status} = TwitterAPI.favorite(user, activity) - response = Poison.encode!(status) - - conn - |> json_reply(200, response) - end - - def unfavorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do - activity = Repo.get(Activity, id) - {:ok, status} = TwitterAPI.unfavorite(user, activity) - response = Poison.encode!(status) - - conn - |> json_reply(200, response) - end - - def retweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do - activity = Repo.get(Activity, id) - if activity.data["actor"] == user.ap_id do - bad_request_reply(conn, "You cannot repeat your own notice.") - else - {:ok, status} = TwitterAPI.retweet(user, activity) - response = Poison.encode!(status) - - conn - - |> json_reply(200, response) - end - end - - defp bad_request_reply(conn, error_message) do - json = error_json(conn, error_message) - json_reply(conn, 400, json) - end - defp json_reply(conn, status, json) do conn |> put_resp_content_type("application/json") |> send_resp(status, json) end - - defp forbidden_json_reply(conn, error_message) do - json = error_json(conn, error_message) - json_reply(conn, 403, json) - end - - defp error_json(conn, error_message) do - %{"error" => error_message, "request" => conn.request_path} |> Poison.encode! - end end diff --git a/lib/pleroma/web/twitter_api/views/attachment_view.ex b/lib/pleroma/web/twitter_api/views/attachment_view.ex new file mode 100644 index 000000000..bb8b6628b --- /dev/null +++ b/lib/pleroma/web/twitter_api/views/attachment_view.ex @@ -0,0 +1,14 @@ +defmodule Pleroma.Web.TwitterAPI.AttachmentView do + use Pleroma.Web, :view + alias Pleroma.Object + + def render("show.json", %{attachment: %Object{data: data}}) do + url = List.first(data["url"]) + %{ + url: url["href"], + mimetype: url["mediaType"], + id: data["uuid"], + oembed: false + } + end +end diff --git a/lib/pleroma/web/twitter_api/views/status_view.ex b/lib/pleroma/web/twitter_api/views/status_view.ex index e69de29bb..9cdecc500 100644 --- a/lib/pleroma/web/twitter_api/views/status_view.ex +++ b/lib/pleroma/web/twitter_api/views/status_view.ex @@ -0,0 +1,155 @@ +defmodule Pleroma.Web.TwitterAPI.StatusView do + use Pleroma.Web, :view + alias Pleroma.{Activity, User, Misc} + alias Calendar.Strftime + alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView, AttachmentView} + + def render( + "show.json", + assigns = %{ + activity: %Activity{ + data: %{"type" => "Announce", "id" => id, "object" => ap_id} + }, + }) do + {activity, user} = render_activity(assigns) + + announced_activity = Activity.get_by_ap_id(ap_id) + text = "#{user.nickname} retweeted a status." + retweeted_status = render("show.json", + Map.merge(assigns, %{activity: announced_activity}) + ) + + Map.merge(activity, %{ + "retweeted_status" => retweeted_status, + "statusnet_html" => text, + "text" => text, + "uri" => "tag:#{id}:objectType=note" + }) + end + + def render( + "show.json", + assigns = %{activity: %Activity{ + data: %{"type" => "Like", "id" => id, "object" => liked_id} + }, + }) do + {activity, %User{nickname: nickname}} = render_activity(assigns) + text = "#{nickname} favorited a status." + + %Activity{id: liked_activity_id} = Activity.get_by_ap_id(liked_id) + + Map.merge(activity, %{ + "in_reply_to_status_id" => liked_activity_id, + "statusnet_html" => text, + "text" => text, + "uri" => "tag#{id}:objectType=Favorite" + }) + end + + def render( + "show.json", + assigns = %{ + activity: %Activity{ + data: %{"type" => "Follow", "object" => followed_id} + } + } + ) do + {activity, %User{nickname: follower_name}} = render_activity(assigns) + %User{nickname: followed_name} = User.get_cached_by_ap_id(followed_id) + text = "#{follower_name} started following #{followed_name}" + + Map.merge(activity, %{ + "statusnet_html" => text, + "text" => text + }) + end + + def render( + "show.json", + assigns = %{ + activity: %Activity{ + data: %{ + "type" => "Create", "to" => to, + "object" => object = %{ + "content" => content + } + } + } + } + ) do + announcement_count = object["announcement_count"] || 0 + repeated = Misc.to_boolean(assigns[:for] && assigns[:for].ap_id in (object["announcements"] || [])) + + like_count = object["like_count"] || 0 + favorited = Misc.to_boolean(assigns[:for] && assigns[:for].ap_id in (object["likes"] || [])) + + mentions = assigns[:mentions] || [] + attentions = to + |> Enum.map(fn (ap_id) -> Enum.find(mentions, fn(user) -> ap_id == user.ap_id end) end) + |> Enum.filter(&Misc.to_boolean/1) + |> Enum.map(fn (user) -> UserView.render("short.json", Map.merge(assigns, %{user: user})) end) + + attachments = (object["attachment"] || []) + {activity, _user} = render_activity(assigns) + Map.merge(activity, %{ + "attachments" => render_many(attachments, AttachmentView, "show.json"), + "attentions" => attentions, + "fave_num" => like_count, + "favorited" => favorited, + "in_reply_to_status_id" => object["inReplyToStatusId"], + "is_post_verb" => true, + "repeat_num" => announcement_count, + "repeated" => repeated, + "statusnet_html" => content, + "text" => HtmlSanitizeEx.strip_tags(content) + }) + end + + def render("timeline.json", assigns = %{activities: activities}) do + render_many(activities, Pleroma.Web.TwitterAPI.StatusView, "show.json", + Map.merge(assigns, %{as: :activity})) + end + + def conversation_id(%Activity{data: %{"context" => context}}) do + if context do + TwitterAPI.context_to_conversation_id(context) + else + nil + end + end + + def conversation_id(%Activity{}), do: nil + + defp render_activity(assigns = %{ + activity: activity = %Activity{ + data: %{"published" => created_at, "id" => external_url, "actor" => actor_id} + }, + }) do + user = User.get_cached_by_ap_id(actor_id) + {%{ + "attachments" => [], + "attentions" => [], + "created_at" => created_at |> date_to_asctime, + "external_url" => external_url, + "fave_num" => 0, + "favorited" => false, + "id" => activity.id, + "in_reply_to_status_id" => nil, + "is_local" => true, + "is_post_verb" => false, + "repeat_num" => 0, + "repeated" => false, + "statusnet_conversation_id" => conversation_id(activity), + "source" => "api", + "user" => UserView.render("show.json", Map.merge(assigns, %{user: user})) + }, user} + end + + defp date_to_asctime(date) do + with {:ok, date, _offset} <- date |> DateTime.from_iso8601 do + Strftime.strftime!(date, "%a %b %d %H:%M:%S %z %Y") + else _e -> + "" + end + end +end diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex index 0b851a60a..60d802b01 100644 --- a/lib/pleroma/web/twitter_api/views/user_view.ex +++ b/lib/pleroma/web/twitter_api/views/user_view.ex @@ -2,10 +2,10 @@ defmodule Pleroma.Web.TwitterAPI.UserView do use Pleroma.Web, :view alias Pleroma.User - def render("show.json", opts = %{user: user = %User{}}) do + def render("show.json", assigns = %{user: user = %User{}}) do image = User.avatar_url(user) - following = if opts[:for] do - User.following?(opts[:for], user) + following = if assigns[:for] do + User.following?(assigns[:for], user) else false end @@ -13,22 +13,33 @@ defmodule Pleroma.Web.TwitterAPI.UserView do user_info = User.get_cached_user_info(user) %{ + "description" => user.bio, + "favourites_count" => 0, + "followers_count" => user_info[:follower_count], + "following" => following, + "friends_count" => user_info[:following_count], "id" => user.id, "name" => user.name, - "screen_name" => user.nickname, - "description" => user.bio, - "following" => following, - # Fake fields - "favourites_count" => 0, - "statuses_count" => user_info[:note_count], - "friends_count" => user_info[:following_count], - "followers_count" => user_info[:follower_count], "profile_image_url" => image, "profile_image_url_https" => image, "profile_image_url_profile_size" => image, "profile_image_url_original" => image, "rights" => %{}, + "screen_name" => user.nickname, + "statuses_count" => user_info[:note_count], "statusnet_profile_url" => user.ap_id } end + + def render("short.json", %{user: %User{ + nickname: nickname, id: id, ap_id: ap_id, name: name + }}) do + %{ + "fullname" => name, + "id" => id, + "ostatus_uri" => ap_id, + "profile_url" => ap_id, + "screen_name" => nickname + } + end end diff --git a/test/support/builders/activity_builder.ex b/test/support/builders/activity_builder.ex index 16011edbf..17ba23749 100644 --- a/test/support/builders/activity_builder.ex +++ b/test/support/builders/activity_builder.ex @@ -11,7 +11,8 @@ defmodule Pleroma.Builders.ActivityBuilder do "object" => %{ "type" => "Note", "content" => "test" - } + }, + "type" => "Create" } Map.merge(activity, data) end diff --git a/test/support/factory.ex b/test/support/factory.ex index 67a6dbb03..dfa5f030a 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -23,7 +23,7 @@ defmodule Pleroma.Factory do "id" => Pleroma.Web.ActivityPub.ActivityPub.generate_object_id, "actor" => user.ap_id, "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "published_at" => Misc.make_date(), + "published" => Misc.make_date(), "likes" => [], "like_count" => 0, "context" => "2hu" @@ -42,7 +42,7 @@ defmodule Pleroma.Factory do "actor" => note.data["actor"], "to" => note.data["to"], "object" => note.data, - "published_at" => Misc.make_date(), + "published" => Misc.make_date(), "context" => note.data["context"] } @@ -60,7 +60,7 @@ defmodule Pleroma.Factory do "actor" => user.ap_id, "type" => "Like", "object" => note_activity.data["object"]["id"], - "published_at" => Misc.make_date() + "published" => Misc.make_date() } %Pleroma.Activity{ @@ -77,7 +77,7 @@ defmodule Pleroma.Factory do "actor" => follower.ap_id, "type" => "Follow", "object" => followed.ap_id, - "published_at" => Misc.make_date() + "published" => Misc.make_date() } %Pleroma.Activity{ diff --git a/test/web/twitter_api/representers/activity_representer_test.exs b/test/web/twitter_api/representers/activity_representer_test.exs deleted file mode 100644 index f4f3c9aba..000000000 --- a/test/web/twitter_api/representers/activity_representer_test.exs +++ /dev/null @@ -1,131 +0,0 @@ -defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenterTest do - use Pleroma.DataCase - alias Pleroma.{User, Activity, Object} - alias Pleroma.Web.TwitterAPI.Representers.{UserRepresenter, ActivityRepresenter, ObjectRepresenter} - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Builders.UserBuilder - import Pleroma.Factory - - test "an announce activity" do - user = insert(:user) - note_activity = insert(:note_activity) - activity_actor = Repo.get_by(User, ap_id: note_activity.data["actor"]) - object = Object.get_by_ap_id(note_activity.data["object"]["id"]) - - {:ok, announce_activity, _object} = ActivityPub.announce(user, object) - note_activity = Activity.get_by_ap_id(note_activity.data["id"]) - - status = ActivityRepresenter.to_map(announce_activity, %{users: [user, activity_actor], announced_activity: note_activity, for: user}) - - assert status["id"] == announce_activity.id - assert status["user"] == UserRepresenter.to_map(user, %{for: user}) - - retweeted_status = ActivityRepresenter.to_map(note_activity, %{user: activity_actor, for: user}) - assert retweeted_status["repeated"] == true - assert retweeted_status["id"] == note_activity.id - assert status["statusnet_conversation_id"] == retweeted_status["statusnet_conversation_id"] - - assert status["retweeted_status"] == retweeted_status - end - - test "a like activity" do - user = insert(:user) - note_activity = insert(:note_activity) - object = Object.get_by_ap_id(note_activity.data["object"]["id"]) - - {:ok, like_activity, _object} = ActivityPub.like(user, object) - status = ActivityRepresenter.to_map(like_activity, %{user: user, liked_activity: note_activity}) - - assert status["id"] == like_activity.id - assert status["in_reply_to_status_id"] == note_activity.id - - note_activity = Activity.get_by_ap_id(note_activity.data["id"]) - activity_actor = Repo.get_by(User, ap_id: note_activity.data["actor"]) - liked_status = ActivityRepresenter.to_map(note_activity, %{user: activity_actor, for: user}) - assert liked_status["favorited"] == true - end - - test "an activity" do - {:ok, user} = UserBuilder.insert - # {:ok, mentioned_user } = UserBuilder.insert(%{nickname: "shp", ap_id: "shp"}) - mentioned_user = insert(:user, %{nickname: "shp"}) - - # {:ok, follower} = UserBuilder.insert(%{following: [User.ap_followers(user)]}) - follower = insert(:user, %{following: [User.ap_followers(user)]}) - - object = %Object{ - data: %{ - "type" => "Image", - "url" => [ - %{ - "type" => "Link", - "mediaType" => "image/jpg", - "href" => "http://example.org/image.jpg" - } - ], - "uuid" => 1 - } - } - - content_html = "Some content mentioning @shp" - content = HtmlSanitizeEx.strip_tags(content_html) - date = DateTime.from_naive!(~N[2016-05-24 13:26:08.003], "Etc/UTC") |> DateTime.to_iso8601 - - {:ok, convo_object} = Object.context_mapping("2hu") |> Repo.insert - - activity = %Activity{ - id: 1, - data: %{ - "type" => "Create", - "id" => "id", - "to" => [ - User.ap_followers(user), - "https://www.w3.org/ns/activitystreams#Public", - mentioned_user.ap_id - ], - "actor" => User.ap_id(user), - "object" => %{ - "published" => date, - "type" => "Note", - "content" => content_html, - "inReplyToStatusId" => 213123, - "attachment" => [ - object - ], - "like_count" => 5, - "announcement_count" => 3, - "context" => "2hu" - }, - "published" => date, - "context" => "2hu" - } - } - - - expected_status = %{ - "id" => activity.id, - "user" => UserRepresenter.to_map(user, %{for: follower}), - "is_local" => true, - "attentions" => [], - "statusnet_html" => content_html, - "text" => content, - "is_post_verb" => true, - "created_at" => "Tue May 24 13:26:08 +0000 2016", - "in_reply_to_status_id" => 213123, - "statusnet_conversation_id" => convo_object.id, - "attachments" => [ - ObjectRepresenter.to_map(object) - ], - "attentions" => [ - UserRepresenter.to_map(mentioned_user, %{for: follower}) - ], - "fave_num" => 5, - "repeat_num" => 3, - "favorited" => false, - "repeated" => false, - "external_url" => activity.data["id"] - } - - assert ActivityRepresenter.to_map(activity, %{user: user, for: follower, mentioned: [mentioned_user]}) == expected_status - end -end diff --git a/test/web/twitter_api/representers/object_representer_test.exs b/test/web/twitter_api/representers/object_representer_test.exs deleted file mode 100644 index 791b30237..000000000 --- a/test/web/twitter_api/representers/object_representer_test.exs +++ /dev/null @@ -1,31 +0,0 @@ -defmodule Pleroma.Web.TwitterAPI.Representers.ObjectReprenterTest do - use Pleroma.DataCase - - alias Pleroma.Object - alias Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter - - test "represent an image attachment" do - object = %Object{ - id: 5, - data: %{ - "type" => "Image", - "url" => [ - %{ - "mediaType" => "sometype", - "href" => "someurl" - } - ], - "uuid" => 6 - } - } - - expected_object = %{ - id: 6, - url: "someurl", - mimetype: "sometype", - oembed: false - } - - assert expected_object == ObjectRepresenter.to_map(object) - end -end diff --git a/test/web/twitter_api/representers/user_representer_test.exs b/test/web/twitter_api/representers/user_representer_test.exs deleted file mode 100644 index 77f065948..000000000 --- a/test/web/twitter_api/representers/user_representer_test.exs +++ /dev/null @@ -1,82 +0,0 @@ -defmodule Pleroma.Web.TwitterAPI.Representers.UserRepresenterTest do - use Pleroma.DataCase - - alias Pleroma.User - alias Pleroma.Web.TwitterAPI.Representers.UserRepresenter - alias Pleroma.Builders.UserBuilder - - import Pleroma.Factory - - setup do - user = insert(:user) - [user: user] - end - - test "A user with an avatar object", %{user: user} do - image = "image" - user = %{ user | avatar: %{ "url" => [%{"href" => image}] }} - represented = UserRepresenter.to_map(user) - assert represented["profile_image_url"] == image - end - - test "A user" do - note_activity = insert(:note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - follower = insert(:user) - second_follower = insert(:user) - - User.follow(follower, user) - User.follow(second_follower, user) - User.follow(user, follower) - - user = Repo.get!(User, user.id) - - image = "https://placehold.it/48x48" - - represented = %{ - "id" => user.id, - "name" => user.name, - "screen_name" => user.nickname, - "description" => user.bio, - # Fake fields - "favourites_count" => 0, - "statuses_count" => 1, - "friends_count" => 1, - "followers_count" => 2, - "profile_image_url" => image, - "profile_image_url_https" => image, - "profile_image_url_profile_size" => image, - "profile_image_url_original" => image, - "following" => false, - "rights" => %{}, - "statusnet_profile_url" => user.ap_id - } - - assert represented == UserRepresenter.to_map(user) - end - - test "A user for a given other follower", %{user: user} do - {:ok, follower} = UserBuilder.insert(%{following: [User.ap_followers(user)]}) - image = "https://placehold.it/48x48" - represented = %{ - "id" => user.id, - "name" => user.name, - "screen_name" => user.nickname, - "description" => user.bio, - # Fake fields - "favourites_count" => 0, - "statuses_count" => 0, - "friends_count" => 0, - "followers_count" => 1, - "profile_image_url" => image, - "profile_image_url_https" => image, - "profile_image_url_profile_size" => image, - "profile_image_url_original" => image, - "following" => true, - "rights" => %{}, - "statusnet_profile_url" => user.ap_id - } - - assert represented == UserRepresenter.to_map(user, %{for: follower}) - end -end diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index 1f2569a73..a3d236d9f 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -1,9 +1,9 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do use Pleroma.Web.ConnCase - alias Pleroma.Web.TwitterAPI.Representers.{UserRepresenter, ActivityRepresenter} alias Pleroma.Builders.{ActivityBuilder, UserBuilder} alias Pleroma.{Repo, Activity, User, Object} alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.TwitterAPI.{UserView, StatusView} import Pleroma.Factory @@ -19,7 +19,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do |> with_credentials(user.nickname, "test") |> post("/api/account/verify_credentials.json") - assert json_response(conn, 200) == UserRepresenter.to_map(user) + assert json_response(conn, 200) == UserView.render("show.json", %{user: user}) end end @@ -46,7 +46,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do assert json_response(conn, 400) == error_response conn = conn_with_creds |> post(request_path, %{ status: "Nice meme." }) - assert json_response(conn, 200) == ActivityRepresenter.to_map(Repo.one(Activity), %{user: user}) + assert json_response(conn, 200) == StatusView.render("show.json", %{activity: Repo.one(Activity)}) end end @@ -70,14 +70,13 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do test "returns one status", %{conn: conn} do {:ok, user} = UserBuilder.insert {:ok, activity} = ActivityBuilder.insert(%{}, %{user: user}) - actor = Repo.get_by!(User, ap_id: activity.data["actor"]) conn = conn |> get("/api/statuses/show/#{activity.id}.json") response = json_response(conn, 200) - assert response == ActivityRepresenter.to_map(activity, %{user: actor}) + assert response == StatusView.render("show.json", %{activity: activity}) end end @@ -122,7 +121,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do response = json_response(conn, 200) assert length(response) == 10 - assert response == Enum.map(returned_activities, fn (activity) -> ActivityRepresenter.to_map(activity, %{user: User.get_cached_by_ap_id(activity.data["actor"]), for: current_user}) end) + assert response == StatusView.render("timeline.json", %{activities: returned_activities, for: current_user}) end end @@ -143,7 +142,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do response = json_response(conn, 200) assert length(response) == 1 - assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: current_user, mentioned: [current_user]}) + assert Enum.at(response, 0) == StatusView.render("show.json", %{activity: activity}) end end @@ -161,7 +160,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do conn = get(conn, "/api/statuses/user_timeline.json", %{"user_id" => user.id}) response = json_response(conn, 200) assert length(response) == 1 - assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user}) + assert Enum.at(response, 0) == StatusView.render("show.json", %{activity: activity}) end test "with screen_name", %{conn: conn} do @@ -171,7 +170,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do conn = get(conn, "/api/statuses/user_timeline.json", %{"screen_name" => user.nickname}) response = json_response(conn, 200) assert length(response) == 1 - assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user}) + assert Enum.at(response, 0) == StatusView.render("show.json", %{activity: activity}) end test "with credentials", %{conn: conn, user: current_user} do @@ -183,7 +182,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do response = json_response(conn, 200) assert length(response) == 1 - assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: current_user}) + assert Enum.at(response, 0) == StatusView.render("show.json", %{activity: activity}) end test "with credentials with user_id", %{conn: conn, user: current_user} do @@ -196,7 +195,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do response = json_response(conn, 200) assert length(response) == 1 - assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user}) + assert Enum.at(response, 0) == StatusView.render("show.json", %{activity: activity}) end test "with credentials screen_name", %{conn: conn, user: current_user} do @@ -209,7 +208,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do response = json_response(conn, 200) assert length(response) == 1 - assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user}) + assert Enum.at(response, 0) == StatusView.render("show.json", %{activity: activity}) end end @@ -229,7 +228,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do current_user = Repo.get(User, current_user.id) assert current_user.following == [User.ap_followers(followed)] - assert json_response(conn, 200) == UserRepresenter.to_map(followed, %{for: current_user}) + assert json_response(conn, 200) == UserView.render("show.json", %{user: followed, for: current_user}) end end @@ -253,7 +252,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do current_user = Repo.get(User, current_user.id) assert current_user.following == [] - assert json_response(conn, 200) == UserRepresenter.to_map(followed, %{for: current_user}) + assert json_response(conn, 200) == UserView.render("show.json", %{user: followed, for: current_user}) end end @@ -278,7 +277,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do current_user = Repo.get(User, current_user.id) assert is_map(current_user.avatar) - assert json_response(conn, 200) == UserRepresenter.to_map(current_user, %{for: current_user}) + assert json_response(conn, 200) == UserView.render("show.json", %{user: current_user, for: current_user}) end end @@ -339,15 +338,14 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do response = conn |> with_credentials(user.nickname, "test") |> post(request_path) - assert json_response(response, 400) == %{"error" => "You cannot repeat your own notice.", + assert json_response(response, 400) == %{"error" => "You cannot repeat your own status.", "request" => request_path} response = conn |> with_credentials(current_user.nickname, "test") |> post(request_path) activity = Repo.get(Activity, note_activity.id) - activity_user = Repo.get_by(User, ap_id: note_activity.data["actor"]) - assert json_response(response, 200) == ActivityRepresenter.to_map(activity, %{user: activity_user, for: current_user}) + assert json_response(response, 200) == StatusView.render("show.json", %{activity: activity, for: current_user}) end end @@ -368,7 +366,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do user = json_response(conn, 200) fetched_user = Repo.get_by(User, nickname: "lain") - assert user == UserRepresenter.to_map(fetched_user) + assert user == UserView.render("show.json", %{user: fetched_user}) end test "it returns errors on a problem", %{conn: conn} do diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs index 0e6bdabbb..b2c58eb29 100644 --- a/test/web/twitter_api/twitter_api_test.exs +++ b/test/web/twitter_api/twitter_api_test.exs @@ -1,10 +1,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do use Pleroma.DataCase - alias Pleroma.Builders.{UserBuilder, ActivityBuilder} + alias Pleroma.Builders.{UserBuilder} alias Pleroma.Web.TwitterAPI.TwitterAPI alias Pleroma.{Activity, User, Object, Repo} - alias Pleroma.Web.TwitterAPI.Representers.{ActivityRepresenter, UserRepresenter} - alias Pleroma.Web.ActivityPub.ActivityPub import Pleroma.Factory @@ -73,113 +71,6 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do assert Enum.member?(get_in(reply.data, ["to"]), "some_cool_id") end - test "fetch public statuses, excluding remote ones." do - %{ public: activity, user: user } = ActivityBuilder.public_and_non_public - insert(:note_activity, %{local: false}) - - follower = insert(:user, following: [User.ap_followers(user)]) - - statuses = TwitterAPI.fetch_public_statuses(follower) - - assert length(statuses) == 1 - assert Enum.at(statuses, 0) == ActivityRepresenter.to_map(activity, %{user: user, for: follower}) - end - - test "fetch whole known network statuses" do - %{ public: activity, user: user } = ActivityBuilder.public_and_non_public - insert(:note_activity, %{local: false}) - - follower = insert(:user, following: [User.ap_followers(user)]) - - statuses = TwitterAPI.fetch_public_and_external_statuses(follower) - - assert length(statuses) == 2 - assert Enum.at(statuses, 0) == ActivityRepresenter.to_map(activity, %{user: user, for: follower}) - end - - test "fetch friends' statuses" do - user = insert(:user, %{following: ["someguy/followers"]}) - {:ok, activity} = ActivityBuilder.insert(%{"to" => ["someguy/followers"]}) - {:ok, direct_activity} = ActivityBuilder.insert(%{"to" => [user.ap_id]}) - - statuses = TwitterAPI.fetch_friend_statuses(user) - - activity_user = Repo.get_by(User, ap_id: activity.data["actor"]) - direct_activity_user = Repo.get_by(User, ap_id: direct_activity.data["actor"]) - - assert length(statuses) == 2 - assert Enum.at(statuses, 0) == ActivityRepresenter.to_map(activity, %{user: activity_user}) - assert Enum.at(statuses, 1) == ActivityRepresenter.to_map(direct_activity, %{user: direct_activity_user, mentioned: [user]}) - end - - test "fetch user's mentions" do - user = insert(:user) - {:ok, activity} = ActivityBuilder.insert(%{"to" => [user.ap_id]}) - activity_user = Repo.get_by(User, ap_id: activity.data["actor"]) - - statuses = TwitterAPI.fetch_mentions(user) - - assert length(statuses) == 1 - assert Enum.at(statuses, 0) == ActivityRepresenter.to_map(activity, %{user: activity_user, mentioned: [user]}) - end - - test "get a user by params" do - user1_result = {:ok, user1} = UserBuilder.insert(%{ap_id: "some id", email: "test@pleroma"}) - {:ok, user2} = UserBuilder.insert(%{ap_id: "some other id", nickname: "testname2", email: "test2@pleroma"}) - - assert {:error, "You need to specify screen_name or user_id"} == TwitterAPI.get_user(nil, nil) - assert user1_result == TwitterAPI.get_user(nil, %{"user_id" => user1.id}) - assert user1_result == TwitterAPI.get_user(nil, %{"screen_name" => user1.nickname}) - assert user1_result == TwitterAPI.get_user(user1, nil) - assert user1_result == TwitterAPI.get_user(user2, %{"user_id" => user1.id}) - assert user1_result == TwitterAPI.get_user(user2, %{"screen_name" => user1.nickname}) - assert {:error, "No user with such screen_name"} == TwitterAPI.get_user(nil, %{"screen_name" => "Satan"}) - assert {:error, "No user with such user_id"} == TwitterAPI.get_user(nil, %{"user_id" => 666}) - end - - test "fetch user's statuses" do - {:ok, user1} = UserBuilder.insert(%{ap_id: "some id", email: "test@pleroma"}) - {:ok, user2} = UserBuilder.insert(%{ap_id: "some other id", nickname: "testname2", email: "test2@pleroma"}) - - {:ok, status1} = ActivityBuilder.insert(%{"id" => 1}, %{user: user1}) - {:ok, status2} = ActivityBuilder.insert(%{"id" => 2}, %{user: user2}) - - user1_statuses = TwitterAPI.fetch_user_statuses(user1, %{"actor_id" => user1.ap_id}) - - assert length(user1_statuses) == 1 - assert Enum.at(user1_statuses, 0) == ActivityRepresenter.to_map(status1, %{user: user1}) - - user2_statuses = TwitterAPI.fetch_user_statuses(user1, %{"actor_id" => user2.ap_id}) - - assert length(user2_statuses) == 1 - assert Enum.at(user2_statuses, 0) == ActivityRepresenter.to_map(status2, %{user: user2}) - end - - test "fetch a single status" do - {:ok, activity} = ActivityBuilder.insert() - {:ok, user} = UserBuilder.insert() - actor = Repo.get_by!(User, ap_id: activity.data["actor"]) - - status = TwitterAPI.fetch_status(user, activity.id) - - assert status == ActivityRepresenter.to_map(activity, %{for: user, user: actor}) - end - - test "fetch statuses in a context using the conversation id" do - {:ok, user} = UserBuilder.insert() - {:ok, activity} = ActivityBuilder.insert(%{"context" => "2hu"}) - {:ok, activity_two} = ActivityBuilder.insert(%{"context" => "2hu"}) - {:ok, _activity_three} = ActivityBuilder.insert(%{"context" => "3hu"}) - - {:ok, object} = Object.context_mapping("2hu") |> Repo.insert - - statuses = TwitterAPI.fetch_conversation(user, object.id) - - assert length(statuses) == 2 - assert Enum.at(statuses, 0)["id"] == activity.id - assert Enum.at(statuses, 1)["id"] == activity_two.id - end - test "upload a file" do file = %Plug.Upload{content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg"} @@ -214,51 +105,6 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do assert TwitterAPI.add_user_links(text, mentions) == expected_text end - test "it favorites a status, returns the updated status" do - user = insert(:user) - note_activity = insert(:note_activity) - activity_user = Repo.get_by!(User, ap_id: note_activity.data["actor"]) - - {:ok, status} = TwitterAPI.favorite(user, note_activity) - updated_activity = Activity.get_by_ap_id(note_activity.data["id"]) - - assert status == ActivityRepresenter.to_map(updated_activity, %{user: activity_user, for: user}) - end - - test "it unfavorites a status, returns the updated status" do - user = insert(:user) - note_activity = insert(:note_activity) - activity_user = Repo.get_by!(User, ap_id: note_activity.data["actor"]) - object = Object.get_by_ap_id(note_activity.data["object"]["id"]) - - {:ok, _like_activity, _object } = ActivityPub.like(user, object) - updated_activity = Activity.get_by_ap_id(note_activity.data["id"]) - assert ActivityRepresenter.to_map(updated_activity, %{user: activity_user, for: user})["fave_num"] == 1 - - {:ok, status} = TwitterAPI.unfavorite(user, note_activity) - - assert status["fave_num"] == 0 - end - - test "it retweets a status and returns the retweet" do - user = insert(:user) - note_activity = insert(:note_activity) - activity_user = Repo.get_by!(User, ap_id: note_activity.data["actor"]) - - {:ok, status} = TwitterAPI.retweet(user, note_activity) - updated_activity = Activity.get_by_ap_id(note_activity.data["id"]) - - assert status == ActivityRepresenter.to_map(updated_activity, %{user: activity_user, for: user}) - end - - test "it assigns an integer conversation_id" do - note_activity = insert(:note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - status = ActivityRepresenter.to_map(note_activity, %{user: user}) - - assert is_number(status["statusnet_conversation_id"]) - end - setup do Supervisor.terminate_child(Pleroma.Supervisor, Cachex) Supervisor.restart_child(Pleroma.Supervisor, Cachex)