Merge branch 'features/validators-audio' into 'develop'

Pipeline Ingestion: Audio

See merge request pleroma/pleroma!2897
This commit is contained in:
lain 2020-08-18 12:27:45 +00:00
commit 67ec8e87ff
9 changed files with 184 additions and 54 deletions

View file

@ -125,8 +125,8 @@ def fetch_object_from_id(id, options \\ []) do
defp prepare_activity_params(data) do defp prepare_activity_params(data) do
%{ %{
"type" => "Create", "type" => "Create",
"to" => data["to"], "to" => data["to"] || [],
"cc" => data["cc"], "cc" => data["cc"] || [],
# Should we seriously keep this attributedTo thing? # Should we seriously keep this attributedTo thing?
"actor" => data["actor"] || data["attributedTo"], "actor" => data["actor"] || data["attributedTo"],
"object" => data "object" => data

View file

@ -16,6 +16,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
alias Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator alias Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator alias Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
@ -137,6 +138,16 @@ def validate(%{"type" => "Question"} = object, meta) do
end end
end end
def validate(%{"type" => "Audio"} = object, meta) do
with {:ok, object} <-
object
|> AudioValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
object = stringify_keys(object)
{:ok, object, meta}
end
end
def validate(%{"type" => "Answer"} = object, meta) do def validate(%{"type" => "Answer"} = object, meta) do
with {:ok, object} <- with {:ok, object} <-
object object
@ -176,7 +187,7 @@ def validate(
%{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity, %{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,
meta meta
) )
when objtype in ["Question", "Answer"] do when objtype in ~w[Question Answer Audio] do
with {:ok, object_data} <- cast_and_apply(object), with {:ok, object_data} <- cast_and_apply(object),
meta = Keyword.put(meta, :object_data, object_data |> stringify_keys), meta = Keyword.put(meta, :object_data, object_data |> stringify_keys),
{:ok, create_activity} <- {:ok, create_activity} <-
@ -210,6 +221,10 @@ def cast_and_apply(%{"type" => "Answer"} = object) do
AnswerValidator.cast_and_apply(object) AnswerValidator.cast_and_apply(object)
end end
def cast_and_apply(%{"type" => "Audio"} = object) do
AudioValidator.cast_and_apply(object)
end
def cast_and_apply(o), do: {:error, {:validator_not_set, o}} def cast_and_apply(o), do: {:error, {:validator_not_set, o}}
# is_struct/1 isn't present in Elixir 1.8.x # is_struct/1 isn't present in Elixir 1.8.x

View file

@ -0,0 +1,92 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator do
use Ecto.Schema
alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
import Ecto.Changeset
@primary_key false
@derive Jason.Encoder
embedded_schema do
field(:id, ObjectValidators.ObjectID, primary_key: true)
field(:to, ObjectValidators.Recipients, default: [])
field(:cc, ObjectValidators.Recipients, default: [])
field(:bto, ObjectValidators.Recipients, default: [])
field(:bcc, ObjectValidators.Recipients, default: [])
# TODO: Write type
field(:tag, {:array, :map}, default: [])
field(:type, :string)
field(:content, :string)
field(:context, :string)
# TODO: Remove actor on objects
field(:actor, ObjectValidators.ObjectID)
field(:attributedTo, ObjectValidators.ObjectID)
field(:summary, :string)
field(:published, ObjectValidators.DateTime)
# TODO: Write type
field(:emoji, :map, default: %{})
field(:sensitive, :boolean, default: false)
embeds_many(:attachment, AttachmentValidator)
field(:replies_count, :integer, default: 0)
field(:like_count, :integer, default: 0)
field(:announcement_count, :integer, default: 0)
field(:inReplyTo, :string)
field(:uri, ObjectValidators.Uri)
# short identifier for PleromaFE to group statuses by context
field(:context_id, :integer)
field(:likes, {:array, :string}, default: [])
field(:announcements, {:array, :string}, default: [])
end
def cast_and_apply(data) do
data
|> cast_data
|> apply_action(:insert)
end
def cast_and_validate(data) do
data
|> cast_data()
|> validate_data()
end
def cast_data(data) do
%__MODULE__{}
|> changeset(data)
end
defp fix(data) do
data
|> CommonFixes.fix_defaults()
|> CommonFixes.fix_attribution()
end
def changeset(struct, data) do
data = fix(data)
struct
|> cast(data, __schema__(:fields) -- [:attachment])
|> cast_embed(:attachment)
end
def validate_data(data_cng) do
data_cng
|> validate_inclusion(:type, ["Audio"])
|> validate_required([:id, :actor, :attributedTo, :type, :context])
|> CommonValidations.validate_any_presence([:cc, :to])
|> CommonValidations.validate_fields_match([:actor, :attributedTo])
|> CommonValidations.validate_actor_presence()
|> CommonValidations.validate_host_match()
end
end

View file

@ -0,0 +1,22 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
alias Pleroma.Web.ActivityPub.Utils
# based on Pleroma.Web.ActivityPub.Utils.lazy_put_objects_defaults
def fix_defaults(data) do
%{data: %{"id" => context}, id: context_id} =
Utils.create_context(data["context"] || data["conversation"])
data
|> Map.put_new("context", context)
|> Map.put_new("context_id", context_id)
end
def fix_attribution(data) do
data
|> Map.put_new("actor", data["attributedTo"])
end
end

View file

@ -7,9 +7,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do
alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionOptionsValidator alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionOptionsValidator
alias Pleroma.Web.ActivityPub.Utils
import Ecto.Changeset import Ecto.Changeset
@ -81,27 +81,11 @@ defp fix_closed(data) do
end end
end end
# based on Pleroma.Web.ActivityPub.Utils.lazy_put_objects_defaults
defp fix_defaults(data) do
%{data: %{"id" => context}, id: context_id} =
Utils.create_context(data["context"] || data["conversation"])
data
|> Map.put_new_lazy("published", &Utils.make_date/0)
|> Map.put_new("context", context)
|> Map.put_new("context_id", context_id)
end
defp fix_attribution(data) do
data
|> Map.put_new("actor", data["attributedTo"])
end
defp fix(data) do defp fix(data) do
data data
|> fix_attribution() |> CommonFixes.fix_defaults()
|> CommonFixes.fix_attribution()
|> fix_closed() |> fix_closed()
|> fix_defaults()
end end
def changeset(struct, data) do def changeset(struct, data) do

View file

@ -340,7 +340,8 @@ def handle_object_creation(%{"type" => "Answer"} = object_map, meta) do
end end
end end
def handle_object_creation(%{"type" => "Question"} = object, meta) do def handle_object_creation(%{"type" => objtype} = object, meta)
when objtype in ~w[Audio Question] do
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
{:ok, object, meta} {:ok, object, meta}
end end

View file

@ -461,7 +461,7 @@ def handle_incoming(
%{"type" => "Create", "object" => %{"type" => objtype} = object} = data, %{"type" => "Create", "object" => %{"type" => objtype} = object} = data,
options options
) )
when objtype in ["Article", "Event", "Note", "Video", "Page", "Audio"] do when objtype in ~w{Article Event Note Video Page} do
actor = Containment.get_actor(data) actor = Containment.get_actor(data)
with nil <- Activity.get_create_by_object_ap_id(object["id"]), with nil <- Activity.get_create_by_object_ap_id(object["id"]),
@ -555,7 +555,7 @@ def handle_incoming(
%{"type" => "Create", "object" => %{"type" => objtype}} = data, %{"type" => "Create", "object" => %{"type" => objtype}} = data,
_options _options
) )
when objtype in ["Question", "Answer", "ChatMessage"] do when objtype in ~w{Question Answer ChatMessage Audio} do
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data), with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
{:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
{:ok, activity} {:ok, activity}

View file

@ -0,0 +1,45 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.Transmogrifier.AudioHandlingTest do
use Oban.Testing, repo: Pleroma.Repo
use Pleroma.DataCase
alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.Transmogrifier
import Pleroma.Factory
test "it works for incoming listens" do
_user = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
data = %{
"@context" => "https://www.w3.org/ns/activitystreams",
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"cc" => [],
"type" => "Listen",
"id" => "http://mastodon.example.org/users/admin/listens/1234/activity",
"actor" => "http://mastodon.example.org/users/admin",
"object" => %{
"type" => "Audio",
"id" => "http://mastodon.example.org/users/admin/listens/1234",
"attributedTo" => "http://mastodon.example.org/users/admin",
"title" => "lain radio episode 1",
"artist" => "lain",
"album" => "lain radio",
"length" => 180_000
}
}
{:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
object = Object.normalize(activity)
assert object.data["title"] == "lain radio episode 1"
assert object.data["artist"] == "lain"
assert object.data["album"] == "lain radio"
assert object.data["length"] == 180_000
end
end

View file

@ -225,35 +225,6 @@ test "it works for incoming notices with hashtags" do
assert Enum.at(object.data["tag"], 2) == "moo" assert Enum.at(object.data["tag"], 2) == "moo"
end end
test "it works for incoming listens" do
data = %{
"@context" => "https://www.w3.org/ns/activitystreams",
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"cc" => [],
"type" => "Listen",
"id" => "http://mastodon.example.org/users/admin/listens/1234/activity",
"actor" => "http://mastodon.example.org/users/admin",
"object" => %{
"type" => "Audio",
"id" => "http://mastodon.example.org/users/admin/listens/1234",
"attributedTo" => "http://mastodon.example.org/users/admin",
"title" => "lain radio episode 1",
"artist" => "lain",
"album" => "lain radio",
"length" => 180_000
}
}
{:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
object = Object.normalize(activity)
assert object.data["title"] == "lain radio episode 1"
assert object.data["artist"] == "lain"
assert object.data["album"] == "lain radio"
assert object.data["length"] == 180_000
end
test "it works for incoming notices with contentMap" do test "it works for incoming notices with contentMap" do
data = data =
File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Poison.decode!() File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Poison.decode!()