소스 검색

Merge branch 'feature/masto_api_markers' into 'develop'

[#1275] Markers /api/v1/markers

See merge request pleroma/pleroma!1852
merge-requests/1875/head
kaniini 4 년 전
부모
커밋
ce750dbab5
10개의 변경된 파일353개의 추가작업 그리고 0개의 파일을 삭제
  1. +1
    -0
      CHANGELOG.md
  2. +74
    -0
      lib/pleroma/marker.ex
  3. +32
    -0
      lib/pleroma/web/mastodon_api/controllers/marker_controller.ex
  4. +17
    -0
      lib/pleroma/web/mastodon_api/views/marker_view.ex
  5. +3
    -0
      lib/pleroma/web/router.ex
  6. +15
    -0
      priv/repo/migrations/20191014181019_create_markers.exs
  7. +51
    -0
      test/marker_test.exs
  8. +9
    -0
      test/support/factory.ex
  9. +124
    -0
      test/web/mastodon_api/controllers/marker_controller_test.exs
  10. +27
    -0
      test/web/mastodon_api/views/marker_view_test.exs

+ 1
- 0
CHANGELOG.md 파일 보기

@@ -51,6 +51,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Admin API: `POST/DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group` are deprecated in favor of: `POST/DELETE /api/pleroma/admin/users/permission_group/:permission_group` (both accept `nicknames` array), `DELETE /api/pleroma/admin/users` (`nickname` query param or `nickname` sent in JSON body) is deprecated in favor of: `DELETE /api/pleroma/admin/users` (`nicknames` query array param or `nicknames` sent in JSON body).
- Admin API: Add `GET /api/pleroma/admin/relay` endpoint - lists all followed relays
- Pleroma API: `POST /api/v1/pleroma/conversations/read` to mark all conversations as read
- Mastodon API: Add `/api/v1/markers` for managing timeline read markers

### Changed
- **Breaking:** Elixir >=1.8 is now required (was >= 1.7)


+ 74
- 0
lib/pleroma/marker.ex 파일 보기

@@ -0,0 +1,74 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Marker do
use Ecto.Schema

import Ecto.Changeset
import Ecto.Query

alias Ecto.Multi
alias Pleroma.Repo
alias Pleroma.User

@timelines ["notifications"]

schema "markers" do
field(:last_read_id, :string, default: "")
field(:timeline, :string, default: "")
field(:lock_version, :integer, default: 0)

belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
timestamps()
end

def get_markers(user, timelines \\ []) do
Repo.all(get_query(user, timelines))
end

def upsert(%User{} = user, attrs) do
attrs
|> Map.take(@timelines)
|> Enum.reduce(Multi.new(), fn {timeline, timeline_attrs}, multi ->
marker =
user
|> get_marker(timeline)
|> changeset(timeline_attrs)

Multi.insert(multi, timeline, marker,
returning: true,
on_conflict: {:replace, [:last_read_id]},
conflict_target: [:user_id, :timeline]
)
end)
|> Repo.transaction()
end

defp get_marker(user, timeline) do
case Repo.find_resource(get_query(user, timeline)) do
{:ok, marker} -> %__MODULE__{marker | user: user}
_ -> %__MODULE__{timeline: timeline, user_id: user.id}
end
end

@doc false
defp changeset(marker, attrs) do
marker
|> cast(attrs, [:last_read_id])
|> validate_required([:user_id, :timeline, :last_read_id])
|> validate_inclusion(:timeline, @timelines)
end

defp by_timeline(query, timeline) do
from(m in query, where: m.timeline in ^List.wrap(timeline))
end

defp by_user_id(query, id), do: from(m in query, where: m.user_id == ^id)

defp get_query(user, timelines) do
__MODULE__
|> by_user_id(user.id)
|> by_timeline(timelines)
end
end

+ 32
- 0
lib/pleroma/web/mastodon_api/controllers/marker_controller.ex 파일 보기

@@ -0,0 +1,32 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.MastodonAPI.MarkerController do
use Pleroma.Web, :controller
alias Pleroma.Plugs.OAuthScopesPlug

plug(
OAuthScopesPlug,
%{scopes: ["read:statuses"]}
when action == :index
)

plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :upsert)
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)

# GET /api/v1/markers
def index(%{assigns: %{user: user}} = conn, params) do
markers = Pleroma.Marker.get_markers(user, params["timeline"])
render(conn, "markers.json", %{markers: markers})
end

# POST /api/v1/markers
def upsert(%{assigns: %{user: user}} = conn, params) do
with {:ok, result} <- Pleroma.Marker.upsert(user, params),
markers <- Map.values(result) do
render(conn, "markers.json", %{markers: markers})
end
end
end

+ 17
- 0
lib/pleroma/web/mastodon_api/views/marker_view.ex 파일 보기

@@ -0,0 +1,17 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.MastodonAPI.MarkerView do
use Pleroma.Web, :view

def render("markers.json", %{markers: markers}) do
Enum.reduce(markers, %{}, fn m, acc ->
Map.put_new(acc, m.timeline, %{
last_read_id: m.last_read_id,
version: m.lock_version,
updated_at: NaiveDateTime.to_iso8601(m.updated_at)
})
end)
end
end

+ 3
- 0
lib/pleroma/web/router.ex 파일 보기

@@ -405,6 +405,9 @@ defmodule Pleroma.Web.Router do
get("/push/subscription", SubscriptionController, :get)
put("/push/subscription", SubscriptionController, :update)
delete("/push/subscription", SubscriptionController, :delete)

get("/markers", MarkerController, :index)
post("/markers", MarkerController, :upsert)
end

scope "/api/web", Pleroma.Web do


+ 15
- 0
priv/repo/migrations/20191014181019_create_markers.exs 파일 보기

@@ -0,0 +1,15 @@
defmodule Pleroma.Repo.Migrations.CreateMarkers do
use Ecto.Migration

def change do
create_if_not_exists table(:markers) do
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
add(:timeline, :string, default: "", null: false)
add(:last_read_id, :string, default: "", null: false)
add(:lock_version, :integer, default: 0, null: false)
timestamps()
end

create_if_not_exists(unique_index(:markers, [:user_id, :timeline]))
end
end

+ 51
- 0
test/marker_test.exs 파일 보기

@@ -0,0 +1,51 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.MarkerTest do
use Pleroma.DataCase
alias Pleroma.Marker

import Pleroma.Factory

describe "get_markers/2" do
test "returns user markers" do
user = insert(:user)
marker = insert(:marker, user: user)
insert(:marker, timeline: "home", user: user)
assert Marker.get_markers(user, ["notifications"]) == [refresh_record(marker)]
end
end

describe "upsert/2" do
test "creates a marker" do
user = insert(:user)

{:ok, %{"notifications" => %Marker{} = marker}} =
Marker.upsert(
user,
%{"notifications" => %{"last_read_id" => "34"}}
)

assert marker.timeline == "notifications"
assert marker.last_read_id == "34"
assert marker.lock_version == 0
end

test "updates exist marker" do
user = insert(:user)
marker = insert(:marker, user: user, last_read_id: "8909")

{:ok, %{"notifications" => %Marker{}}} =
Marker.upsert(
user,
%{"notifications" => %{"last_read_id" => "9909"}}
)

marker = refresh_record(marker)
assert marker.timeline == "notifications"
assert marker.last_read_id == "9909"
assert marker.lock_version == 0
end
end
end

+ 9
- 0
test/support/factory.ex 파일 보기

@@ -377,4 +377,13 @@ defmodule Pleroma.Factory do
)
}
end

def marker_factory do
%Pleroma.Marker{
user: build(:user),
timeline: "notifications",
lock_version: 0,
last_read_id: "1"
}
end
end

+ 124
- 0
test/web/mastodon_api/controllers/marker_controller_test.exs 파일 보기

@@ -0,0 +1,124 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.MastodonAPI.MarkerControllerTest do
use Pleroma.Web.ConnCase

import Pleroma.Factory

describe "GET /api/v1/markers" do
test "gets markers with correct scopes", %{conn: conn} do
user = insert(:user)
token = insert(:oauth_token, user: user, scopes: ["read:statuses"])

{:ok, %{"notifications" => marker}} =
Pleroma.Marker.upsert(
user,
%{"notifications" => %{"last_read_id" => "69420"}}
)

response =
conn
|> assign(:user, user)
|> assign(:token, token)
|> get("/api/v1/markers", %{timeline: ["notifications"]})
|> json_response(200)

assert response == %{
"notifications" => %{
"last_read_id" => "69420",
"updated_at" => NaiveDateTime.to_iso8601(marker.updated_at),
"version" => 0
}
}
end

test "gets markers with missed scopes", %{conn: conn} do
user = insert(:user)
token = insert(:oauth_token, user: user, scopes: [])

Pleroma.Marker.upsert(user, %{"notifications" => %{"last_read_id" => "69420"}})

response =
conn
|> assign(:user, user)
|> assign(:token, token)
|> get("/api/v1/markers", %{timeline: ["notifications"]})
|> json_response(403)

assert response == %{"error" => "Insufficient permissions: read:statuses."}
end
end

describe "POST /api/v1/markers" do
test "creates a marker with correct scopes", %{conn: conn} do
user = insert(:user)
token = insert(:oauth_token, user: user, scopes: ["write:statuses"])

response =
conn
|> assign(:user, user)
|> assign(:token, token)
|> post("/api/v1/markers", %{
home: %{last_read_id: "777"},
notifications: %{"last_read_id" => "69420"}
})
|> json_response(200)

assert %{
"notifications" => %{
"last_read_id" => "69420",
"updated_at" => _,
"version" => 0
}
} = response
end

test "updates exist marker", %{conn: conn} do
user = insert(:user)
token = insert(:oauth_token, user: user, scopes: ["write:statuses"])

{:ok, %{"notifications" => marker}} =
Pleroma.Marker.upsert(
user,
%{"notifications" => %{"last_read_id" => "69477"}}
)

response =
conn
|> assign(:user, user)
|> assign(:token, token)
|> post("/api/v1/markers", %{
home: %{last_read_id: "777"},
notifications: %{"last_read_id" => "69888"}
})
|> json_response(200)

assert response == %{
"notifications" => %{
"last_read_id" => "69888",
"updated_at" => NaiveDateTime.to_iso8601(marker.updated_at),
"version" => 0
}
}
end

test "creates a marker with missed scopes", %{conn: conn} do
user = insert(:user)
token = insert(:oauth_token, user: user, scopes: [])

response =
conn
|> assign(:user, user)
|> assign(:token, token)
|> post("/api/v1/markers", %{
home: %{last_read_id: "777"},
notifications: %{"last_read_id" => "69420"}
})
|> json_response(403)

assert response == %{"error" => "Insufficient permissions: write:statuses."}
end
end
end

+ 27
- 0
test/web/mastodon_api/views/marker_view_test.exs 파일 보기

@@ -0,0 +1,27 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.MastodonAPI.MarkerViewTest do
use Pleroma.DataCase
alias Pleroma.Web.MastodonAPI.MarkerView
import Pleroma.Factory

test "returns markers" do
marker1 = insert(:marker, timeline: "notifications", last_read_id: "17")
marker2 = insert(:marker, timeline: "home", last_read_id: "42")

assert MarkerView.render("markers.json", %{markers: [marker1, marker2]}) == %{
"home" => %{
last_read_id: "42",
updated_at: NaiveDateTime.to_iso8601(marker2.updated_at),
version: 0
},
"notifications" => %{
last_read_id: "17",
updated_at: NaiveDateTime.to_iso8601(marker1.updated_at),
version: 0
}
}
end
end

불러오는 중...
취소
저장