More tests

This commit is contained in:
Thomas Citharel 2018-08-24 11:34:00 +02:00
parent a3852f26c1
commit 686cf04787
29 changed files with 945 additions and 241 deletions

View File

@ -3,5 +3,5 @@ defmodule Eventos.Activity do
Represents an activity Represents an activity
""" """
defstruct [:id, :data, :local, :actor, :recipients, :notifications] defstruct [:id, :data, :local, :actor, :recipients, :notifications, :type]
end end

View File

@ -173,23 +173,9 @@ defmodule Eventos.Actors.Actor do
|> put_change(:local, true) |> put_change(:local, true)
end end
def get_or_fetch_by_url(url) do
if user = Actors.get_actor_by_url(url) do
user
else
case ActivityPub.make_actor_from_url(url) do
{:ok, user} ->
user
_ ->
{:error, "Could not fetch by AP id"}
end
end
end
@spec get_public_key_for_url(String.t()) :: {:ok, String.t()} @spec get_public_key_for_url(String.t()) :: {:ok, String.t()}
def get_public_key_for_url(url) do def get_public_key_for_url(url) do
with %Actor{} = actor <- get_or_fetch_by_url(url) do with {:ok, %Actor{} = actor} <- Actors.get_or_fetch_by_url(url) do
actor.keys actor.keys
|> Eventos.Service.ActivityPub.Utils.pem_to_public_key() |> Eventos.Service.ActivityPub.Utils.pem_to_public_key()
else else
@ -228,4 +214,44 @@ defmodule Eventos.Actors.Actor do
) )
) )
end end
def get_groups_member_of(%Actor{id: actor_id}) do
Repo.all(
from(
a in Actor,
join: m in Member,
on: a.id == m.parent_id,
where: m.actor_id == ^actor_id
)
)
end
def get_members_for_group(%Actor{id: actor_id}) do
Repo.all(
from(
a in Actor,
join: m in Member,
on: a.id == m.actor_id,
where: m.parent_id == ^actor_id
)
)
end
def follow(%Actor{} = follower, %Actor{} = followed) do
# Check if actor is locked
# Check if followed has blocked follower
# Check if follower already follows followed
cond do
following?(follower, followed) ->
{:error,
"Could not follow actor: you are already following #{followed.preferred_username}"}
# true -> nil
# Follow the person
end
end
def following?(%Actor{} = follower, %Actor{followers: followers}) do
Enum.member?(followers, follower)
end
end end

View File

@ -265,13 +265,11 @@ defmodule Eventos.Actors do
def get_or_fetch_by_url(url) do def get_or_fetch_by_url(url) do
if actor = get_actor_by_url(url) do if actor = get_actor_by_url(url) do
actor {:ok, actor}
else else
ap_try = ActivityPub.make_actor_from_url(url) case ActivityPub.make_actor_from_url(url) do
case ap_try do
{:ok, actor} -> {:ok, actor} ->
actor {:ok, actor}
_ -> _ ->
{:error, "Could not fetch by AP id"} {:error, "Could not fetch by AP id"}
@ -299,7 +297,7 @@ defmodule Eventos.Actors do
@doc """ @doc """
Find actors by their name or displayed name Find actors by their name or displayed name
""" """
def find_actors_by_username(username) do def find_actors_by_username_or_name(username) do
Repo.all( Repo.all(
from( from(
a in Actor, a in Actor,
@ -320,7 +318,7 @@ defmodule Eventos.Actors do
@email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ @email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
def search(name) do def search(name) do
# find already saved accounts # find already saved accounts
case find_actors_by_username(name) do case find_actors_by_username_or_name(name) do
[] -> [] ->
# no accounts found, let's test if it's an username@domain.tld # no accounts found, let's test if it's an username@domain.tld
with true <- Regex.match?(@email_regex, name), with true <- Regex.match?(@email_regex, name),
@ -457,6 +455,24 @@ defmodule Eventos.Actors do
|> Repo.insert() |> Repo.insert()
end end
@doc """
Gets an user by it's email
## Examples
iex> get_user_by_email(user, email)
{:ok, %User{}}
iex> get_user_by_email(user, wrong_email)
{:error, nil}
"""
def get_user_by_email(email) do
case Repo.get_by(User, email: email) do
nil -> {:error, nil}
user -> {:ok, user}
end
end
@doc """ @doc """
Updates a user. Updates a user.
@ -548,10 +564,12 @@ defmodule Eventos.Actors do
""" """
def create_member(attrs \\ %{}) do def create_member(attrs \\ %{}) do
%Member{} with {:ok, %Member{} = member} <-
|> Member.changeset(attrs) %Member{}
|> Repo.insert!() |> Member.changeset(attrs)
|> Repo.preload([:actor, :parent]) |> Repo.insert() do
{:ok, Repo.preload(member, [:actor, :parent])}
end
end end
@doc """ @doc """

View File

@ -7,7 +7,6 @@ defmodule Eventos.Actors.Member do
alias Eventos.Actors.Member alias Eventos.Actors.Member
alias Eventos.Actors.Actor alias Eventos.Actors.Actor
@primary_key false
schema "members" do schema "members" do
field(:approved, :boolean, default: true) field(:approved, :boolean, default: true)
# 0 : Member, 1 : Moderator, 2 : Admin # 0 : Member, 1 : Moderator, 2 : Admin
@ -23,5 +22,6 @@ defmodule Eventos.Actors.Member do
member member
|> cast(attrs, [:role, :approved, :parent_id, :actor_id]) |> cast(attrs, [:role, :approved, :parent_id, :actor_id])
|> validate_required([:parent_id, :actor_id]) |> validate_required([:parent_id, :actor_id])
|> unique_constraint(:parent_id, name: :members_actor_parent_unique_index)
end end
end end

View File

@ -11,16 +11,18 @@ defmodule Eventos.Actors.Service.ResetPassword do
""" """
@spec check_reset_password_token(String.t(), String.t()) :: tuple @spec check_reset_password_token(String.t(), String.t()) :: tuple
def check_reset_password_token(password, token) do def check_reset_password_token(password, token) do
with %User{} = user <- Repo.get_by(User, reset_password_token: token) do with %User{} = user <- Repo.get_by(User, reset_password_token: token),
Repo.update( {:ok, %User{} = user} <-
User.password_reset_changeset(user, %{ Repo.update(
"password" => password, User.password_reset_changeset(user, %{
"reset_password_sent_at" => nil, "password" => password,
"reset_password_token" => nil "reset_password_sent_at" => nil,
}) "reset_password_token" => nil
) })
) do
{:ok, Repo.preload(user, :actors)}
else else
_err -> err ->
{:error, :invalid_token} {:error, :invalid_token}
end end
end end

View File

@ -21,17 +21,23 @@ defmodule Eventos.Events.Comment do
belongs_to(:in_reply_to_comment, Comment, foreign_key: :in_reply_to_comment_id) belongs_to(:in_reply_to_comment, Comment, foreign_key: :in_reply_to_comment_id)
belongs_to(:origin_comment, Comment, foreign_key: :origin_comment_id) belongs_to(:origin_comment, Comment, foreign_key: :origin_comment_id)
timestamps() timestamps(type: :utc_datetime)
end end
@doc false @doc false
def changeset(comment, attrs) do def changeset(comment, attrs) do
uuid = Ecto.UUID.generate() uuid = Ecto.UUID.generate()
# TODO : really change me right away
url =
if Map.has_key?(attrs, "url"),
do: attrs["url"],
else: "#{EventosWeb.Endpoint.url()}/comments/#{uuid}"
comment comment
|> cast(attrs, [:url, :text, :actor_id, :event_id, :in_reply_to_comment_id, :attributed_to_id]) |> cast(attrs, [:url, :text, :actor_id, :event_id, :in_reply_to_comment_id, :attributed_to_id])
|> validate_required([:text, :actor_id])
|> put_change(:uuid, uuid) |> put_change(:uuid, uuid)
|> put_change(:url, "#{EventosWeb.Endpoint.url()}/comments/#{uuid}") |> put_change(:url, url)
|> validate_required([:text, :actor_id, :url])
end end
end end

View File

@ -18,8 +18,11 @@ defmodule Eventos.Events.Event do
field(:description, :string) field(:description, :string)
field(:ends_on, Timex.Ecto.DateTimeWithTimezone) field(:ends_on, Timex.Ecto.DateTimeWithTimezone)
field(:title, :string) field(:title, :string)
# ???
field(:state, :integer, default: 0) field(:state, :integer, default: 0)
# Event status: TENTATIVE 1, CONFIRMED 2, CANCELLED 3
field(:status, :integer, default: 0) field(:status, :integer, default: 0)
# If the event is public or private
field(:public, :boolean, default: true) field(:public, :boolean, default: true)
field(:thumbnail, :string) field(:thumbnail, :string)
field(:large_image, :string) field(:large_image, :string)
@ -42,9 +45,7 @@ defmodule Eventos.Events.Event do
@doc false @doc false
def changeset(%Event{} = event, attrs) do def changeset(%Event{} = event, attrs) do
uuid = Ecto.UUID.generate() # TODO : Change all of this
# TODO : check what's the use here. Tests ?
actor_url = actor_url =
if Map.has_key?(attrs, :organizer_actor) do if Map.has_key?(attrs, :organizer_actor) do
attrs.organizer_actor.preferred_username attrs.organizer_actor.preferred_username
@ -52,6 +53,13 @@ defmodule Eventos.Events.Event do
"" ""
end end
uuid = Ecto.UUID.generate()
url =
if Map.has_key?(attrs, "url"),
do: attrs["url"],
else: "#{EventosWeb.Endpoint.url()}/@#{actor_url}/#{uuid}"
event event
|> Ecto.Changeset.cast(attrs, [ |> Ecto.Changeset.cast(attrs, [
:title, :title,
@ -74,7 +82,7 @@ defmodule Eventos.Events.Event do
|> cast_assoc(:tags) |> cast_assoc(:tags)
|> cast_assoc(:physical_address) |> cast_assoc(:physical_address)
|> put_change(:uuid, uuid) |> put_change(:uuid, uuid)
|> put_change(:url, "#{EventosWeb.Endpoint.url()}/@#{actor_url}/#{uuid}") |> put_change(:url, url)
|> validate_required([ |> validate_required([
:title, :title,
:begins_on, :begins_on,

View File

@ -110,10 +110,17 @@ defmodule Eventos.Events do
@doc """ @doc """
Gets an event by it's URL Gets an event by it's URL
""" """
def get_event_by_url!(url) do def get_event_by_url(url) do
Repo.get_by(Event, url: url) Repo.get_by(Event, url: url)
end end
@doc """
Gets an event by it's URL
"""
def get_event_by_url!(url) do
Repo.get_by!(Event, url: url)
end
@doc """ @doc """
Gets an event by it's UUID Gets an event by it's UUID
""" """
@ -175,7 +182,10 @@ defmodule Eventos.Events do
@doc """ @doc """
Find events by name Find events by name
""" """
def find_events_by_name(name) when name == "", do: []
def find_events_by_name(name) do def find_events_by_name(name) do
name = String.trim(name)
events = Repo.all(from(a in Event, where: ilike(a.title, ^like_sanitize(name)))) events = Repo.all(from(a in Event, where: ilike(a.title, ^like_sanitize(name))))
Repo.preload(events, [:organizer_actor]) Repo.preload(events, [:organizer_actor])
end end
@ -780,6 +790,32 @@ defmodule Eventos.Events do
Repo.all(Comment) Repo.all(Comment)
end end
def get_comments_for_actor(%Actor{id: actor_id}, page \\ 1, limit \\ 10) do
start = (page - 1) * limit
query =
from(
c in Comment,
where: c.actor_id == ^actor_id,
limit: ^limit,
order_by: [desc: :id],
offset: ^start,
preload: [
:actor,
:in_reply_to_comment,
:origin_comment,
:event
]
)
comments = Repo.all(query)
count_comments =
Repo.one(from(c in Comment, select: count(c.id), where: c.actor_id == ^actor_id))
{:ok, comments, count_comments}
end
@doc """ @doc """
Gets a single comment. Gets a single comment.
@ -798,6 +834,16 @@ defmodule Eventos.Events do
def get_comment_with_uuid!(uuid), do: Repo.get_by!(Comment, uuid: uuid) def get_comment_with_uuid!(uuid), do: Repo.get_by!(Comment, uuid: uuid)
def get_comment_from_url(url), do: Repo.get_by(Comment, url: url)
def get_comment_from_url!(url), do: Repo.get_by!(Comment, url: url)
def get_comment_full_from_url!(url) do
with %Comment{} = comment <- Repo.get_by!(Comment, url: url) do
Repo.preload(comment, :actor)
end
end
@doc """ @doc """
Creates a comment. Creates a comment.

View File

@ -18,10 +18,16 @@ defmodule EventosWeb.ActivityPubController do
end end
def event(conn, %{"uuid" => uuid}) do def event(conn, %{"uuid" => uuid}) do
with %Event{} = event <- Events.get_event_full_by_uuid(uuid) do with %Event{} = event <- Events.get_event_full_by_uuid(uuid),
true <- event.public do
conn conn
|> put_resp_header("content-type", "application/activity+json") |> put_resp_header("content-type", "application/activity+json")
|> json(ObjectView.render("event.json", %{event: event})) |> json(ObjectView.render("event.json", %{event: event}))
else
false ->
conn
|> put_status(404)
|> json("Not found")
end end
end end

View File

@ -37,8 +37,12 @@ defmodule EventosWeb.ActorController do
end end
def show(conn, %{"name" => name}) do def show(conn, %{"name" => name}) do
actor = Actors.get_actor_by_name_with_everything(name) with %Actor{} = actor <- Actors.get_actor_by_name_with_everything(name) do
render(conn, "show.json", actor: actor) render(conn, "show.json", actor: actor)
else
nil ->
send_resp(conn, :not_found, "")
end
end end
@email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ @email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
@ -57,7 +61,7 @@ defmodule EventosWeb.ActorController do
actor = Actors.get_local_actor_by_name(name) actor = Actors.get_local_actor_by_name(name)
with {:ok, %Actor{} = actor} <- Actors.update_actor(actor, actor_params) do with {:ok, %Actor{} = actor} <- Actors.update_actor(actor, actor_params) do
render(conn, "show.json", actor: actor) render(conn, "show_basic.json", actor: actor)
end end
end end

View File

@ -15,15 +15,13 @@ defmodule EventosWeb.GroupController do
end end
def create(conn, %{"group" => group_params}) do def create(conn, %{"group" => group_params}) do
with {:ok, %Actor{} = group} <- Actors.create_group(group_params) do with {:ok, %Actor{} = group} <- Actors.create_group(group_params),
%Member{} = {:ok, %Member{} = member} <-
_member = Actors.create_member(%{
Actors.create_member(%{ "parent_id" => group.id,
"parent_id" => group.id, "actor_id" => Actors.get_local_actor_by_name(group_params["actor_admin"]).id,
"actor_id" => Actors.get_local_actor_by_name(group_params["actor_admin"]).id, "role" => 2
"role" => 2 }) do
})
conn conn
|> put_status(:created) |> put_status(:created)
|> put_resp_header("location", actor_path(conn, :show, group)) |> put_resp_header("location", actor_path(conn, :show, group))
@ -34,7 +32,7 @@ defmodule EventosWeb.GroupController do
def join(conn, %{"name" => group_name, "actor_name" => actor_name}) do def join(conn, %{"name" => group_name, "actor_name" => actor_name}) do
with %Actor{} = group <- Actors.get_group_by_name(group_name), with %Actor{} = group <- Actors.get_group_by_name(group_name),
%Actor{} = actor <- Actors.get_local_actor_by_name(actor_name), %Actor{} = actor <- Actors.get_local_actor_by_name(actor_name),
%Member{} = member <- {:ok, %Member{} = member} <-
Actors.create_member(%{"parent_id" => group.id, "actor_id" => actor.id}) do Actors.create_member(%{"parent_id" => group.id, "actor_id" => actor.id}) do
conn conn
|> put_status(:created) |> put_status(:created)

View File

@ -42,11 +42,15 @@ defmodule EventosWeb.UserController do
end end
end end
@time_before_resend 3600
def resend_confirmation(conn, %{"email" => email}) do def resend_confirmation(conn, %{"email" => email}) do
with {:ok, %User{} = user} <- Actors.find_by_email(email), with {:ok, %User{} = user} <- Actors.find_by_email(email),
false <- is_nil(user.confirmation_token), false <- is_nil(user.confirmation_token),
true <- true <-
Timex.before?(Timex.shift(user.confirmation_sent_at, hours: 1), DateTime.utc_now()) do Timex.before?(
Timex.shift(user.confirmation_sent_at, seconds: @time_before_resend),
DateTime.utc_now()
) do
Activation.resend_confirmation_email(user) Activation.resend_confirmation_email(user)
render(conn, "confirmation.json", %{user: user}) render(conn, "confirmation.json", %{user: user})
else else
@ -58,7 +62,10 @@ defmodule EventosWeb.UserController do
_ -> _ ->
conn conn
|> put_status(:not_found) |> put_status(:not_found)
|> json(%{"error" => "Unable to resend the validation token"}) |> json(%{
"error" =>
"Unable to resend the validation token. Please wait a while before you can ask for resending token"
})
end end
end end
@ -67,7 +74,7 @@ defmodule EventosWeb.UserController do
{:ok, _} <- ResetPassword.send_password_reset_email(user) do {:ok, _} <- ResetPassword.send_password_reset_email(user) do
render(conn, "password_reset.json", %{user: user}) render(conn, "password_reset.json", %{user: user})
else else
{:error, :not_found} -> {:error, nil} ->
conn conn
|> put_status(:not_found) |> put_status(:not_found)
|> json(%{"errors" => "Unable to find an user with this email"}) |> json(%{"errors" => "Unable to find an user with this email"})
@ -105,23 +112,23 @@ defmodule EventosWeb.UserController do
render(conn, "show_simple.json", user: user) render(conn, "show_simple.json", user: user)
end end
defp handle_changeset_errors(errors) do # defp handle_changeset_errors(errors) do
errors # errors
|> Enum.map(fn {field, detail} -> # |> Enum.map(fn {field, detail} ->
"#{field} " <> render_detail(detail) # "#{field} " <> render_detail(detail)
end) # end)
|> Enum.join() # |> Enum.join()
end # end
defp render_detail({message, values}) do # defp render_detail({message, values}) do
Enum.reduce(values, message, fn {k, v}, acc -> # Enum.reduce(values, message, fn {k, v}, acc ->
String.replace(acc, "%{#{k}}", to_string(v)) # String.replace(acc, "%{#{k}}", to_string(v))
end) # end)
end # end
defp render_detail(message) do # defp render_detail(message) do
message # message
end # end
def update(conn, %{"id" => id, "user" => user_params}) do def update(conn, %{"id" => id, "user" => user_params}) do
user = Actors.get_user!(id) user = Actors.get_user!(id)

View File

@ -122,6 +122,7 @@ defmodule EventosWeb.Router do
get("/@:name/following", ActivityPubController, :following) get("/@:name/following", ActivityPubController, :following)
get("/@:name/followers", ActivityPubController, :followers) get("/@:name/followers", ActivityPubController, :followers)
get("/events/:uuid", ActivityPubController, :event) get("/events/:uuid", ActivityPubController, :event)
get("/comments/:uuid", ActivityPubController, :event)
post("/@:name/inbox", ActivityPubController, :inbox) post("/@:name/inbox", ActivityPubController, :inbox)
post("/inbox", ActivityPubController, :inbox) post("/inbox", ActivityPubController, :inbox)
end end

View File

@ -134,10 +134,17 @@ defmodule EventosWeb.ActivityPub.ActorView do
else else
"Announce" "Announce"
end, end,
"actor" => activity.data.organizer_actor.url, "actor" => activity.actor,
"published" => Timex.now(), "published" => Timex.now(),
"to" => ["https://www.w3.org/ns/activitystreams#Public"], "to" => ["https://www.w3.org/ns/activitystreams#Public"],
"object" => render_one(activity.data, ObjectView, "event.json", as: :event) "object" =>
case activity.type do
:Event ->
render_one(activity.data, ObjectView, "event.json", as: :event)
:Comment ->
render_one(activity.data, ObjectView, "note.json", as: :note)
end
} }
end end

View File

@ -32,6 +32,19 @@ defmodule EventosWeb.ActivityPub.ObjectView do
Map.merge(event, @base) Map.merge(event, @base)
end end
def render("note.json", %{note: note}) do
event = %{
"type" => "Note",
"id" => note.url,
"content" => note.text,
"mediaType" => "text/markdown",
"published" => Timex.format!(note.inserted_at, "{ISO:Extended}"),
"updated" => Timex.format!(note.updated_at, "{ISO:Extended}")
}
Map.merge(event, @base)
end
def render("category.json", %{category: category}) do def render("category.json", %{category: category}) do
%{"title" => category.title} %{"title" => category.title}
end end

View File

@ -6,7 +6,7 @@ defmodule Eventos.Service.ActivityPub do
""" """
alias Eventos.Events alias Eventos.Events
alias Eventos.Events.{Event, Category} alias Eventos.Events.{Event, Category, Comment}
alias Eventos.Service.ActivityPub.Transmogrifier alias Eventos.Service.ActivityPub.Transmogrifier
alias Eventos.Service.WebFinger alias Eventos.Service.WebFinger
alias Eventos.Activity alias Eventos.Activity
@ -44,36 +44,65 @@ defmodule Eventos.Service.ActivityPub do
end end
end end
def fetch_event_from_url(url) do def fetch_object_from_url(url, :event), do: fetch_event_from_url(url)
if object = Events.get_event_by_url!(url) do def fetch_object_from_url(url, :note), do: fetch_note_from_url(url)
{:ok, object}
@spec fetch_object_from_url(String.t()) :: tuple()
def fetch_object_from_url(url) do
with true <- String.starts_with?(url, "http"),
{:ok, %{body: body, status_code: code}} when code in 200..299 <-
HTTPoison.get(
url,
[Accept: "application/activity+json"],
follow_redirect: true,
timeout: 10_000,
recv_timeout: 20_000
),
{:ok, data} <- Jason.decode(body),
nil <- Events.get_event_by_url(data["id"]),
nil <- Events.get_comment_from_url(data["id"]),
params <- %{
"type" => "Create",
"to" => data["to"],
"cc" => data["cc"],
"actor" => data["attributedTo"],
"object" => data
},
{:ok, activity} <- Transmogrifier.handle_incoming(params) do
case data["type"] do
"Event" ->
{:ok, Events.get_event_by_url!(activity.data["object"]["id"])}
"Note" ->
{:ok, Events.get_comment_from_url!(activity.data["object"]["id"])}
end
else else
object = %Event{} -> {:ok, object}
object = %Comment{} -> {:ok, object}
e -> {:error, e}
end
end
@spec fetch_object_from_url(String.t()) :: tuple()
def fetch_event_from_url(url) do
with nil <- Events.get_event_by_url(url) do
Logger.info("Fetching #{url} via AP")
fetch_object_from_url(url)
else
%Event{} = comment ->
{:ok, comment}
end
end
@spec fetch_object_from_url(String.t()) :: tuple()
def fetch_note_from_url(url) do
with nil <- Events.get_comment_from_url(url) do
Logger.info("Fetching #{url} via AP") Logger.info("Fetching #{url} via AP")
with true <- String.starts_with?(url, "http"), fetch_object_from_url(url)
{:ok, %{body: body, status_code: code}} when code in 200..299 <- else
HTTPoison.get( %Comment{} = comment ->
url, {:ok, comment}
[Accept: "application/activity+json"],
follow_redirect: true,
timeout: 10_000,
recv_timeout: 20_000
),
{:ok, data} <- Jason.decode(body),
nil <- Events.get_event_by_url!(data["id"]),
params <- %{
"type" => "Create",
"to" => data["to"],
"cc" => data["cc"],
"actor" => data["attributedTo"],
"object" => data
},
{:ok, activity} <- Transmogrifier.handle_incoming(params) do
{:ok, Events.get_event_by_url!(activity.data["object"]["id"])}
else
object = %Event{} -> {:ok, object}
e -> e
end
end end
end end
@ -127,7 +156,7 @@ defmodule Eventos.Service.ActivityPub do
end end
end end
def follow(follower, followed, activity_id \\ nil, local \\ true) do def follow(%Actor{} = follower, %Actor{} = followed, activity_id \\ nil, local \\ true) do
with data <- make_follow_data(follower, followed, activity_id), with data <- make_follow_data(follower, followed, activity_id),
{:ok, activity} <- insert(data, local), {:ok, activity} <- insert(data, local),
:ok <- maybe_federate(activity) do :ok <- maybe_federate(activity) do
@ -135,7 +164,9 @@ defmodule Eventos.Service.ActivityPub do
end end
end end
def delete(%Event{url: url, organizer_actor: actor} = event, local \\ true) do def delete(object, local \\ true)
def delete(%Event{url: url, organizer_actor: actor} = event, local) do
data = %{ data = %{
"type" => "Delete", "type" => "Delete",
"actor" => actor.url, "actor" => actor.url,
@ -150,6 +181,21 @@ defmodule Eventos.Service.ActivityPub do
end end
end end
def delete(%Comment{url: url, actor: actor} = comment, local) do
data = %{
"type" => "Delete",
"actor" => actor.url,
"object" => url,
"to" => [actor.url <> "/followers", "https://www.w3.org/ns/activitystreams#Public"]
}
with Events.delete_comment(comment),
{:ok, activity} <- insert(data, local),
:ok <- maybe_federate(activity) do
{:ok, activity}
end
end
def create_public_activities(%Actor{} = actor) do def create_public_activities(%Actor{} = actor) do
end end
@ -285,13 +331,22 @@ defmodule Eventos.Service.ActivityPub do
case actor.type do case actor.type do
:Person -> :Person ->
{:ok, events, total} = Events.get_events_for_actor(actor, page, limit) {:ok, events, total} = Events.get_events_for_actor(actor, page, limit)
{:ok, comments, total} = Events.get_comments_for_actor(actor, page, limit)
activities = event_activities =
Enum.map(events, fn event -> Enum.map(events, fn event ->
{:ok, activity} = event_to_activity(event) {:ok, activity} = event_to_activity(event)
activity activity
end) end)
comment_activities =
Enum.map(comments, fn comment ->
{:ok, activity} = comment_to_activity(comment)
activity
end)
activities = event_activities ++ comment_activities
{activities, total} {activities, total}
:Service -> :Service ->
@ -322,6 +377,7 @@ defmodule Eventos.Service.ActivityPub do
defp event_to_activity(%Event{} = event, local \\ true) do defp event_to_activity(%Event{} = event, local \\ true) do
activity = %Activity{ activity = %Activity{
type: :Event,
data: event, data: event,
local: local, local: local,
actor: event.organizer_actor.url, actor: event.organizer_actor.url,
@ -333,6 +389,20 @@ defmodule Eventos.Service.ActivityPub do
{:ok, activity} {:ok, activity}
end end
defp comment_to_activity(%Comment{} = comment, local \\ true) do
activity = %Activity{
type: :Comment,
data: comment,
local: local,
actor: comment.actor.url,
recipients: ["https://www.w3.org/ns/activitystreams#Public"]
}
# Notification.create_notifications(activity)
# stream_out(activity)
{:ok, activity}
end
defp ical_event_to_activity(%ExIcal.Event{} = ical_event, %Actor{} = actor, source) do defp ical_event_to_activity(%ExIcal.Event{} = ical_event, %Actor{} = actor, source) do
# Logger.debug(inspect ical_event) # Logger.debug(inspect ical_event)
# TODO : refactor me ! # TODO : refactor me !

View File

@ -4,7 +4,7 @@ defmodule Eventos.Service.ActivityPub.Transmogrifier do
""" """
alias Eventos.Actors.Actor alias Eventos.Actors.Actor
alias Eventos.Actors alias Eventos.Actors
alias Eventos.Events.Event alias Eventos.Events.{Event, Comment}
alias Eventos.Service.ActivityPub alias Eventos.Service.ActivityPub
import Ecto.Query import Ecto.Query
@ -77,9 +77,9 @@ defmodule Eventos.Service.ActivityPub.Transmogrifier do
# - tags # - tags
# - emoji # - emoji
def handle_incoming(%{"type" => "Create", "object" => %{"type" => "Note"} = object} = data) do def handle_incoming(%{"type" => "Create", "object" => %{"type" => "Note"} = object} = data) do
Logger.debug("Handle incoming to create notes") Logger.info("Handle incoming to create notes")
with %Actor{} = actor <- Actor.get_or_fetch_by_url(data["actor"]) do with {:ok, %Actor{} = actor} <- Actors.get_or_fetch_by_url(data["actor"]) do
Logger.debug("found actor") Logger.debug("found actor")
object = fix_object(data["object"]) object = fix_object(data["object"])
@ -104,8 +104,8 @@ defmodule Eventos.Service.ActivityPub.Transmogrifier do
def handle_incoming( def handle_incoming(
%{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data %{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data
) do ) do
with %Actor{} = followed <- Actors.get_actor_by_url(followed), with {:ok, %Actor{} = followed} <- Actors.get_or_fetch_by_url(followed),
%Actor{} = follower <- Actors.get_or_fetch_by_url(follower), {:ok, %Actor{} = follower} <- Actors.get_or_fetch_by_url(follower),
{:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do {:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do
ActivityPub.accept(%{to: [follower.url], actor: followed.url, object: data, local: true}) ActivityPub.accept(%{to: [follower.url], actor: followed.url, object: data, local: true})
@ -133,7 +133,7 @@ defmodule Eventos.Service.ActivityPub.Transmogrifier do
def handle_incoming( def handle_incoming(
%{"type" => "Announce", "object" => object_id, "actor" => actor, "id" => id} = data %{"type" => "Announce", "object" => object_id, "actor" => actor, "id" => id} = data
) do ) do
with %Actor{} = actor <- Actors.get_or_fetch_by_url(actor), with {:ok, %Actor{} = actor} <- Actors.get_or_fetch_by_url(actor),
{:ok, object} <- {:ok, object} <-
get_obj_helper(object_id) || ActivityPub.fetch_event_from_url(object_id), get_obj_helper(object_id) || ActivityPub.fetch_event_from_url(object_id),
{:ok, activity, object} <- ActivityPub.announce(actor, object, id, false) do {:ok, activity, object} <- ActivityPub.announce(actor, object, id, false) do
@ -268,6 +268,17 @@ defmodule Eventos.Service.ActivityPub.Transmogrifier do
{:ok, event} {:ok, event}
end end
def prepare_outgoing(%Comment{} = comment) do
comment =
comment
|> Map.from_struct()
|> Map.drop([:__meta__])
|> Map.put(:"@context", "https://www.w3.org/ns/activitystreams")
|> prepare_object
{:ok, comment}
end
# #
# def maybe_fix_object_url(data) do # def maybe_fix_object_url(data) do
# if is_binary(data["object"]) and not String.starts_with?(data["object"], "http") do # if is_binary(data["object"]) and not String.starts_with?(data["object"], "http") do

View File

@ -132,20 +132,23 @@ defmodule Eventos.Service.ActivityPub.Utils do
""" """
def insert_full_object(%{"object" => %{"type" => type} = object_data}) def insert_full_object(%{"object" => %{"type" => type} = object_data})
when is_map(object_data) and type == "Note" do when is_map(object_data) and type == "Note" do
import Logger with {:ok, %Actor{id: actor_id}} <- Actors.get_or_fetch_by_url(object_data["actor"]) do
Logger.debug("insert full object") data = %{
Logger.debug(inspect(object_data)) "text" => object_data["content"],
actor = Actors.get_actor_by_url(object_data["actor"]) "url" => object_data["id"],
"actor_id" => actor_id,
"in_reply_to_comment_id" => object_data["inReplyTo"]
}
data = %{ require Logger
"text" => object_data["content"], Logger.info("comment data ready to be inserted")
"url" => object_data["id"], Logger.info(inspect(data))
"actor_id" => actor.id,
"in_reply_to_comment_id" => object_data["inReplyTo"]
}
with {:ok, _} <- Events.create_comment(data) do with {:ok, comm} <- Events.create_comment(data) do
:ok Logger.info("comment inserted")
Logger.info(inspect(comm))
:ok
end
end end
end end

View File

@ -65,7 +65,6 @@ defmodule Eventos.Mixfile do
{:timex_ecto, "~> 3.0"}, {:timex_ecto, "~> 3.0"},
{:icalendar, "~> 0.6"}, {:icalendar, "~> 0.6"},
{:exgravatar, "~> 2.0.1"}, {:exgravatar, "~> 2.0.1"},
{:littlefinger, "~> 0.1"},
{:httpoison, "~> 1.0"}, {:httpoison, "~> 1.0"},
{:json_ld, "~> 0.2"}, {:json_ld, "~> 0.2"},
{:jason, "~> 1.0"}, {:jason, "~> 1.0"},

View File

@ -0,0 +1,19 @@
defmodule Eventos.Repo.Migrations.AddPrimaryKeyToMember do
use Ecto.Migration
def up do
execute("ALTER TABLE members DROP CONSTRAINT IF EXISTS members_pkey")
drop_if_exists index(:members, ["members_account_id_index"])
create unique_index(:members, [:actor_id, :parent_id], name: :members_actor_parent_unique_index)
alter table(:members) do
add :id, :serial, primary_key: true
end
end
def down do
drop index(:members, [:actor_id, :parent_id], name: :members_actor_parent_unique_index)
alter table(:members) do
remove :id
end
end
end

View File

@ -9,7 +9,7 @@ defmodule Eventos.ActorsTest do
@valid_attrs %{ @valid_attrs %{
summary: "some description", summary: "some description",
name: "some name", name: "Bobby Blank",
domain: "some domain", domain: "some domain",
keys: "some keypair", keys: "some keypair",
suspended: true, suspended: true,
@ -74,7 +74,7 @@ defmodule Eventos.ActorsTest do
end end
test "get_actor_by_name/1 returns a remote actor" do test "get_actor_by_name/1 returns a remote actor" do
assert %Actor{} = actor = Actors.get_or_fetch_by_url(@remote_account_url) assert {:ok, %Actor{} = actor} = Actors.get_or_fetch_by_url(@remote_account_url)
actor_found = Actors.get_actor_by_name("#{actor.preferred_username}@#{actor.domain}") actor_found = Actors.get_actor_by_name("#{actor.preferred_username}@#{actor.domain}")
assert actor_found = actor assert actor_found = actor
end end
@ -107,7 +107,7 @@ defmodule Eventos.ActorsTest do
end end
test "get_actor_by_name_with_everything!/1 returns the remote actor with it's organized events" do test "get_actor_by_name_with_everything!/1 returns the remote actor with it's organized events" do
assert %Actor{} = actor = Actors.get_or_fetch_by_url(@remote_account_url) assert {:ok, %Actor{} = actor} = Actors.get_or_fetch_by_url(@remote_account_url)
assert Actors.get_actor_by_name_with_everything( assert Actors.get_actor_by_name_with_everything(
"#{actor.preferred_username}@#{actor.domain}" "#{actor.preferred_username}@#{actor.domain}"
@ -124,12 +124,15 @@ defmodule Eventos.ActorsTest do
test "get_or_fetch_by_url/1 returns the local actor for the url", %{ test "get_or_fetch_by_url/1 returns the local actor for the url", %{
actor: actor actor: actor
} do } do
assert Actors.get_or_fetch_by_url(actor.url).preferred_username == actor.preferred_username preferred_username = actor.preferred_username
assert Actors.get_or_fetch_by_url(actor.url).domain == nil
assert {:ok, %Actor{preferred_username: preferred_username, domain: nil} = actor_found} =
Actors.get_or_fetch_by_url(actor.url)
end end
test "get_or_fetch_by_url/1 returns the remote actor for the url" do test "get_or_fetch_by_url/1 returns the remote actor for the url" do
assert %Actor{preferred_username: @remote_account_username, domain: @remote_account_domain} = assert {:ok,
%Actor{preferred_username: @remote_account_username, domain: @remote_account_domain}} =
Actors.get_or_fetch_by_url(@remote_account_url) Actors.get_or_fetch_by_url(@remote_account_url)
end end
@ -141,12 +144,21 @@ defmodule Eventos.ActorsTest do
assert actors = [actor, actor2] assert actors = [actor, actor2]
end end
test "test find_actors_by_username/1 returns actors with similar usernames", %{actor: actor} do test "test find_actors_by_username_or_name/1 returns actors with similar usernames", %{
%Actor{} = actor2 = Actors.get_or_fetch_by_url(@remote_account_url) actor: actor
actors = Actors.find_actors_by_username("t") } do
{:ok, %Actor{} = actor2} = Actors.get_or_fetch_by_url(@remote_account_url)
actors = Actors.find_actors_by_username_or_name("t")
assert actors = [actor, actor2] assert actors = [actor, actor2]
end end
test "test find_actors_by_username_or_name/1 returns actors with similar names", %{
actor: actor
} do
actors = Actors.find_actors_by_username_or_name("ohno")
assert actors == []
end
test "test search/1 returns accounts for search with existing accounts", %{actor: actor} do test "test search/1 returns accounts for search with existing accounts", %{actor: actor} do
assert {:ok, [actor]} = Actors.search("t") assert {:ok, [actor]} = Actors.search("t")
end end
@ -180,7 +192,7 @@ defmodule Eventos.ActorsTest do
test "create_actor/1 with valid data creates a actor" do test "create_actor/1 with valid data creates a actor" do
assert {:ok, %Actor{} = actor} = Actors.create_actor(@valid_attrs) assert {:ok, %Actor{} = actor} = Actors.create_actor(@valid_attrs)
assert actor.summary == "some description" assert actor.summary == "some description"
assert actor.name == "some name" assert actor.name == "Bobby Blank"
assert actor.domain == "some domain" assert actor.domain == "some domain"
assert actor.keys == "some keypair" assert actor.keys == "some keypair"
assert actor.suspended assert actor.suspended
@ -484,4 +496,97 @@ defmodule Eventos.ActorsTest do
assert %Ecto.Changeset{} = Actors.change_follower(follower) assert %Ecto.Changeset{} = Actors.change_follower(follower)
end end
end end
describe "members" do
alias Eventos.Actors.Member
alias Eventos.Actors.Actor
@valid_attrs %{approved: true, role: 0}
@update_attrs %{approved: false, role: 1}
@invalid_attrs %{approved: nil, role: nil}
setup do
actor = insert(:actor)
group = insert(:group)
{:ok, actor: actor, group: group}
end
defp create_member(%{actor: actor, group: group}) do
insert(:member, actor: actor, parent: group)
end
test "get_member!/1 returns the member with given id", context do
member = create_member(context)
assert member = Actors.get_member!(member.id)
end
test "create_member/1 with valid data creates a member", %{
actor: actor,
group: group
} do
valid_attrs =
@valid_attrs
|> Map.put(:actor_id, actor.id)
|> Map.put(:parent_id, group.id)
assert {:ok, %Member{} = member} = Actors.create_member(valid_attrs)
assert member.approved == true
assert member.role == 0
assert [group] = Actor.get_groups_member_of(actor)
assert [actor] = Actor.get_members_for_group(group)
end
test "create_member/1 with valid data but same actors fails to create a member", %{
actor: actor,
group: group
} do
create_member(%{actor: actor, group: group})
valid_attrs =
@valid_attrs
|> Map.put(:actor_id, actor.id)
|> Map.put(:parent_id, group.id)
assert {:error, _member} = Actors.create_member(valid_attrs)
end
test "create_member/1 with invalid data returns error changeset", %{
actor: actor,
group: group
} do
invalid_attrs =
@invalid_attrs
|> Map.put(:actor_id, nil)
|> Map.put(:parent_id, nil)
assert {:error, %Ecto.Changeset{}} = Actors.create_member(invalid_attrs)
end
test "update_member/2 with valid data updates the member", context do
member = create_member(context)
assert {:ok, member} = Actors.update_member(member, @update_attrs)
assert %Member{} = member
assert member.approved == false
assert member.role == 1
end
# This can't happen, since attrs are optional
# test "update_member/2 with invalid data returns error changeset", context do
# member = create_member(context)
# assert {:error, %Ecto.Changeset{}} = Actors.update_member(member, @invalid_attrs)
# assert member = Actors.get_member!(member.id)
# end
test "delete_member/1 deletes the member", context do
member = create_member(context)
assert {:ok, %Member{}} = Actors.delete_member(member)
assert_raise Ecto.NoResultsError, fn -> Actors.get_member!(member.id) end
end
test "change_member/1 returns a member changeset", context do
member = create_member(context)
assert %Ecto.Changeset{} = Actors.change_member(member)
end
end
end end

View File

@ -32,6 +32,12 @@ defmodule Eventos.EventsTest do
describe "events" do describe "events" do
alias Eventos.Events.Event alias Eventos.Events.Event
setup do
actor = insert(:actor)
event = insert(:event, organizer_actor: actor)
{:ok, actor: actor, event: event}
end
@valid_attrs %{ @valid_attrs %{
begins_on: "2010-04-17 14:00:00.000000Z", begins_on: "2010-04-17 14:00:00.000000Z",
description: "some description", description: "some description",
@ -46,14 +52,32 @@ defmodule Eventos.EventsTest do
} }
@invalid_attrs %{begins_on: nil, description: nil, ends_on: nil, title: nil} @invalid_attrs %{begins_on: nil, description: nil, ends_on: nil, title: nil}
test "list_events/0 returns all events" do test "list_events/0 returns all events", %{event: event} do
event = event_fixture() assert event.title == hd(Events.list_events()).title
assert hd(Events.list_events()).title == event.title
end end
test "get_event!/1 returns the event with given id" do test "get_event!/1 returns the event with given id", %{event: event} do
event = event_fixture()
assert Events.get_event!(event.id).title == event.title assert Events.get_event!(event.id).title == event.title
refute Ecto.assoc_loaded?(Events.get_event!(event.id).organizer_actor)
end
test "get_event_full!/1 returns the event with given id", %{event: event} do
assert Events.get_event_full!(event.id).organizer_actor.preferred_username ==
event.organizer_actor.preferred_username
assert Events.get_event_full!(event.id).participants == []
end
test "find_events_by_name/1 returns events for a given name", %{event: event} do
assert event.title == hd(Events.find_events_by_name(event.title)).title
event2 = insert(:event, title: "Special event")
assert event2.title == hd(Events.find_events_by_name("Special")).title
event2 = insert(:event, title: "Special event")
assert event2.title == hd(Events.find_events_by_name(" Special ")).title
assert [] == Events.find_events_by_name("")
end end
test "create_event/1 with valid data creates a event" do test "create_event/1 with valid data creates a event" do
@ -79,8 +103,7 @@ defmodule Eventos.EventsTest do
assert {:error, %Ecto.Changeset{}} = Events.create_event(@invalid_attrs) assert {:error, %Ecto.Changeset{}} = Events.create_event(@invalid_attrs)
end end
test "update_event/2 with valid data updates the event" do test "update_event/2 with valid data updates the event", %{event: event} do
event = event_fixture()
assert {:ok, event} = Events.update_event(event, @update_attrs) assert {:ok, event} = Events.update_event(event, @update_attrs)
assert %Event{} = event assert %Event{} = event
assert event.begins_on == DateTime.from_naive!(~N[2011-05-18 15:01:01.000000Z], "Etc/UTC") assert event.begins_on == DateTime.from_naive!(~N[2011-05-18 15:01:01.000000Z], "Etc/UTC")
@ -89,27 +112,44 @@ defmodule Eventos.EventsTest do
assert event.title == "some updated title" assert event.title == "some updated title"
end end
test "update_event/2 with invalid data returns error changeset" do test "update_event/2 with invalid data returns error changeset", %{event: event} do
event = event_fixture()
assert {:error, %Ecto.Changeset{}} = Events.update_event(event, @invalid_attrs) assert {:error, %Ecto.Changeset{}} = Events.update_event(event, @invalid_attrs)
assert event.title == Events.get_event!(event.id).title assert event.title == Events.get_event!(event.id).title
end end
test "delete_event/1 deletes the event" do test "delete_event/1 deletes the event", %{event: event} do
event = event_fixture()
assert {:ok, %Event{}} = Events.delete_event(event) assert {:ok, %Event{}} = Events.delete_event(event)
assert_raise Ecto.NoResultsError, fn -> Events.get_event!(event.id) end assert_raise Ecto.NoResultsError, fn -> Events.get_event!(event.id) end
end end
test "change_event/1 returns a event changeset" do test "change_event/1 returns a event changeset", %{event: event} do
event = event_fixture()
assert %Ecto.Changeset{} = Events.change_event(event) assert %Ecto.Changeset{} = Events.change_event(event)
end end
test "get_events_for_actor/1", %{actor: actor, event: event} do
assert {:ok, [event_found], 1} = Events.get_events_for_actor(actor)
assert event_found.title == event.title
end
test "get_events_for_actor/3", %{actor: actor, event: event} do
event1 = insert(:event, organizer_actor: actor)
assert {:ok, [event_found, event1_found], 2} = Events.get_events_for_actor(actor, 1, 10)
end
test "get_events_for_actor/3 with limited results", %{actor: actor, event: event} do
event1 = insert(:event, organizer_actor: actor)
assert {:ok, [event_found], 2} = Events.get_events_for_actor(actor, 1, 1)
end
end end
describe "categories" do describe "categories" do
alias Eventos.Events.Category alias Eventos.Events.Category
setup do
category = insert(:category)
{:ok, category: category}
end
@valid_attrs %{description: "some description", picture: "some picture", title: "some title"} @valid_attrs %{description: "some description", picture: "some picture", title: "some title"}
@update_attrs %{ @update_attrs %{
description: "some updated description", description: "some updated description",
@ -118,16 +158,18 @@ defmodule Eventos.EventsTest do
} }
@invalid_attrs %{description: nil, picture: nil, title: nil} @invalid_attrs %{description: nil, picture: nil, title: nil}
test "list_categories/0 returns all categories" do test "list_categories/0 returns all categories", %{category: category} do
category = category_fixture()
assert Events.list_categories() == [category] assert Events.list_categories() == [category]
end end
test "get_category!/1 returns the category with given id" do test "get_category!/1 returns the category with given id", %{category: category} do
category = category_fixture()
assert Events.get_category!(category.id) == category assert Events.get_category!(category.id) == category
end end
test "get_category_by_title/1 return the category with given title", %{category: category} do
assert Events.get_category_by_title(category.title) == category
end
test "create_category/1 with valid data creates a category" do test "create_category/1 with valid data creates a category" do
assert {:ok, %Category{} = category} = Events.create_category(@valid_attrs) assert {:ok, %Category{} = category} = Events.create_category(@valid_attrs)
assert category.description == "some description" assert category.description == "some description"
@ -139,8 +181,7 @@ defmodule Eventos.EventsTest do
assert {:error, %Ecto.Changeset{}} = Events.create_category(@invalid_attrs) assert {:error, %Ecto.Changeset{}} = Events.create_category(@invalid_attrs)
end end
test "update_category/2 with valid data updates the category" do test "update_category/2 with valid data updates the category", %{category: category} do
category = category_fixture()
assert {:ok, category} = Events.update_category(category, @update_attrs) assert {:ok, category} = Events.update_category(category, @update_attrs)
assert %Category{} = category assert %Category{} = category
assert category.description == "some updated description" assert category.description == "some updated description"
@ -148,20 +189,17 @@ defmodule Eventos.EventsTest do
assert category.title == "some updated title" assert category.title == "some updated title"
end end
test "update_category/2 with invalid data returns error changeset" do test "update_category/2 with invalid data returns error changeset", %{category: category} do
category = category_fixture()
assert {:error, %Ecto.Changeset{}} = Events.update_category(category, @invalid_attrs) assert {:error, %Ecto.Changeset{}} = Events.update_category(category, @invalid_attrs)
assert category == Events.get_category!(category.id) assert category == Events.get_category!(category.id)
end end
test "delete_category/1 deletes the category" do test "delete_category/1 deletes the category", %{category: category} do
category = category_fixture()
assert {:ok, %Category{}} = Events.delete_category(category) assert {:ok, %Category{}} = Events.delete_category(category)
assert_raise Ecto.NoResultsError, fn -> Events.get_category!(category.id) end assert_raise Ecto.NoResultsError, fn -> Events.get_category!(category.id) end
end end
test "change_category/1 returns a category changeset" do test "change_category/1 returns a category changeset", %{category: category} do
category = category_fixture()
assert %Ecto.Changeset{} = Events.change_category(category) assert %Ecto.Changeset{} = Events.change_category(category)
end end
end end
@ -227,35 +265,31 @@ defmodule Eventos.EventsTest do
end end
describe "participants" do describe "participants" do
alias Eventos.Events.Participant alias Eventos.Events.{Participant, Event}
alias Eventos.Actors.Actor
@valid_attrs %{role: 42} @valid_attrs %{role: 42}
@update_attrs %{role: 43} @update_attrs %{role: 43}
@invalid_attrs %{role: nil} @invalid_attrs %{role: nil}
def participant_fixture(attrs \\ %{}) do setup do
event = event_fixture() actor = insert(:actor)
actor = actor_fixture() event = insert(:event, organizer_actor: actor)
valid_attrs = Map.put(@valid_attrs, :event_id, event.id) participant = insert(:participant, actor: actor, event: event)
valid_attrs = Map.put(valid_attrs, :actor_id, actor.id) {:ok, participant: participant, event: event, actor: actor}
{:ok, participant} =
attrs
|> Enum.into(valid_attrs)
|> Events.create_participant()
participant
end end
test "list_participants/0 returns all participants" do test "list_participants/0 returns all participants", %{participant: participant} do
participant = participant_fixture() assert [%Participant{} = participant] = Events.list_participants()
assert Events.list_participants() == [participant]
end end
# test "get_participant!/1 returns the participant with given id" do test "get_participant!/1 returns the participant for a given event and given actor", %{
# participant = participant_fixture() event: %Event{id: event_id} = _event,
# assert Events.get_participant!(participant.id) == participant actor: %Actor{id: actor_id} = _actor
# end } do
assert %Participant{event_id: event_id, actor_id: actor_id} =
_participant = Events.get_participant!(event_id, actor_id)
end
test "create_participant/1 with valid data creates a participant" do test "create_participant/1 with valid data creates a participant" do
actor = actor_fixture() actor = actor_fixture()
@ -270,25 +304,25 @@ defmodule Eventos.EventsTest do
assert {:error, %Ecto.Changeset{}} = Events.create_participant(@invalid_attrs) assert {:error, %Ecto.Changeset{}} = Events.create_participant(@invalid_attrs)
end end
test "update_participant/2 with valid data updates the participant" do test "update_participant/2 with valid data updates the participant", %{
participant = participant_fixture() participant: participant
} do
assert {:ok, participant} = Events.update_participant(participant, @update_attrs) assert {:ok, participant} = Events.update_participant(participant, @update_attrs)
assert %Participant{} = participant assert %Participant{} = participant
assert participant.role == 43 assert participant.role == 43
end end
test "update_participant/2 with invalid data returns error changeset" do test "update_participant/2 with invalid data returns error changeset", %{
participant = participant_fixture() participant: participant
} do
assert {:error, %Ecto.Changeset{}} = Events.update_participant(participant, @invalid_attrs) assert {:error, %Ecto.Changeset{}} = Events.update_participant(participant, @invalid_attrs)
end end
test "delete_participant/1 deletes the participant" do test "delete_participant/1 deletes the participant", %{participant: participant} do
participant = participant_fixture()
assert {:ok, %Participant{}} = Events.delete_participant(participant) assert {:ok, %Participant{}} = Events.delete_participant(participant)
end end
test "change_participant/1 returns a participant changeset" do test "change_participant/1 returns a participant changeset", %{participant: participant} do
participant = participant_fixture()
assert %Ecto.Changeset{} = Events.change_participant(participant) assert %Ecto.Changeset{} = Events.change_participant(participant)
end end
end end

View File

@ -5,14 +5,20 @@ defmodule Eventos.Service.Activitypub.ActivitypubTest do
alias Eventos.Events alias Eventos.Events
alias Eventos.Actors.Actor alias Eventos.Actors.Actor
alias Eventos.Actors
alias Eventos.Service.ActivityPub alias Eventos.Service.ActivityPub
alias Eventos.Activity alias Eventos.Activity
describe "fetching actor from it's url" do describe "fetching actor from it's url" do
test "returns an actor" do test "returns an actor from nickname" do
assert {:ok, %Actor{preferred_username: "tcit", domain: "framapiaf.org"} = actor} = assert {:ok, %Actor{preferred_username: "tcit", domain: "framapiaf.org"} = actor} =
ActivityPub.make_actor_from_nickname("tcit@framapiaf.org") ActivityPub.make_actor_from_nickname("tcit@framapiaf.org")
end end
test "returns an actor from url" do
assert {:ok, %Actor{preferred_username: "tcit", domain: "framapiaf.org"}} =
Actors.get_or_fetch_by_url("https://framapiaf.org/users/tcit")
end
end end
describe "create activities" do describe "create activities" do
@ -33,8 +39,8 @@ defmodule Eventos.Service.Activitypub.ActivitypubTest do
end end
end end
describe "fetching an object" do describe "fetching an" do
test "it fetches an object" do test "event by url" do
{:ok, object} = {:ok, object} =
ActivityPub.fetch_event_from_url("https://social.tcit.fr/@tcit/99908779444618462") ActivityPub.fetch_event_from_url("https://social.tcit.fr/@tcit/99908779444618462")
@ -55,7 +61,19 @@ defmodule Eventos.Service.Activitypub.ActivitypubTest do
assert delete.data["actor"] == event.organizer_actor.url assert delete.data["actor"] == event.organizer_actor.url
assert delete.data["object"] == event.url assert delete.data["object"] == event.url
assert Events.get_event_by_url!(event.url) == nil assert Events.get_event_by_url(event.url) == nil
end
test "it creates a delete activity and deletes the original comment" do
comment = insert(:comment)
comment = Events.get_comment_full_from_url!(comment.url)
{:ok, delete} = ActivityPub.delete(comment)
assert delete.data["type"] == "Delete"
assert delete.data["actor"] == comment.actor.url
assert delete.data["object"] == comment.url
assert Events.get_comment_from_url(comment.url) == nil
end end
end end

View File

@ -3,10 +3,11 @@ defmodule EventosWeb.ActivityPubControllerTest do
import Eventos.Factory import Eventos.Factory
alias EventosWeb.ActivityPub.{ActorView, ObjectView} alias EventosWeb.ActivityPub.{ActorView, ObjectView}
alias Eventos.{Repo, Actors, Actors.Actor} alias Eventos.{Repo, Actors, Actors.Actor}
alias Eventos.Service.ActivityPub
alias Eventos.Activity alias Eventos.Activity
import Logger import Logger
describe "/@:username" do describe "/@:preferred_username" do
test "it returns a json representation of the actor", %{conn: conn} do test "it returns a json representation of the actor", %{conn: conn} do
actor = insert(:actor) actor = insert(:actor)
@ -22,8 +23,8 @@ defmodule EventosWeb.ActivityPubControllerTest do
end end
end end
describe "/events/uuid" do describe "/events/:uuid" do
test "it returns a json representation of the object", %{conn: conn} do test "it returns a json representation of the event", %{conn: conn} do
event = insert(:event) event = insert(:event)
conn = conn =
@ -34,23 +35,60 @@ defmodule EventosWeb.ActivityPubControllerTest do
assert json_response(conn, 200) == ObjectView.render("event.json", %{event: event}) assert json_response(conn, 200) == ObjectView.render("event.json", %{event: event})
Logger.error(inspect(ObjectView.render("event.json", %{event: event}))) Logger.error(inspect(ObjectView.render("event.json", %{event: event})))
end end
test "it returns 404 for non-public events", %{conn: conn} do
event = insert(:event, public: false)
conn =
conn
|> put_req_header("accept", "application/activity+json")
|> get("/events/#{event.uuid}")
assert json_response(conn, 404)
end
end end
# describe "/actors/:username/inbox" do describe "/@:preferred_username/inbox" do
# test "it inserts an incoming activity into the database", %{conn: conn} do test "it inserts an incoming event into the database", %{conn: conn} do
# data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
#
# conn = conn =
# conn conn
# |> assign(:valid_signature, true) |> assign(:valid_signature, true)
# |> put_req_header("content-type", "application/activity+json") |> put_req_header("content-type", "application/activity+json")
# |> post("/inbox", data) |> post("/inbox", data)
#
# assert "ok" == json_response(conn, 200) assert "ok" == json_response(conn, 200)
# :timer.sleep(500) :timer.sleep(500)
# assert Activity.get_by_ap_id(data["id"]) assert ActivityPub.fetch_object_from_url(data["object"]["id"], :note)
# end end
# end end
describe "/@:preferred_username/outbox" do
test "it returns a note activity in a collection", %{conn: conn} do
actor = insert(:actor)
comment = insert(:comment, actor: actor)
conn =
conn
|> put_req_header("accept", "application/activity+json")
|> get("/@#{actor.preferred_username}/outbox")
assert response(conn, 200) =~ comment.text
end
test "it returns an event activity in a collection", %{conn: conn} do
actor = insert(:actor)
event = insert(:event, organizer_actor: actor)
conn =
conn
|> put_req_header("accept", "application/activity+json")
|> get("/@#{actor.preferred_username}/outbox")
assert response(conn, 200) =~ event.title
end
end
# describe "/actors/:nickname/followers" do # describe "/actors/:nickname/followers" do
# test "it returns the followers in a collection", %{conn: conn} do # test "it returns the followers in a collection", %{conn: conn} do
@ -93,43 +131,43 @@ defmodule EventosWeb.ActivityPubControllerTest do
# end # end
# end # end
# #
# describe "/users/:nickname/following" do # describe "/@:preferred_username/following" do
# test "it returns the following in a collection", %{conn: conn} do # test "it returns the following in a collection", %{conn: conn} do
# user = insert(:user) # actor = insert(:actor)
# user_two = insert(:user) # actor2 = insert(:actor)
# User.follow(user, user_two) # Eventos.Service.ActivityPub.follow(actor, actor2)
#
# result = # result =
# conn # conn
# |> get("/users/#{user.nickname}/following") # |> get("/@#{actor.preferred_username}/following")
# |> json_response(200) # |> json_response(200)
#
# assert result["first"]["orderedItems"] == [user_two.ap_id] # assert result["first"]["orderedItems"] == [actor2.url]
# end # end
#
# test "it works for more than 10 users", %{conn: conn} do # test "it works for more than 10 actors", %{conn: conn} do
# user = insert(:user) # actor = insert(:actor)
#
# Enum.each(1..15, fn _ -> # Enum.each(1..15, fn _ ->
# user = Repo.get(User, user.id) # actor = Repo.get(Actor, actor.id)
# other_user = insert(:user) # other_actor = insert(:actor)
# User.follow(user, other_user) # Actor.follow(actor, other_actor)
# end) # end)
#
# result = # result =
# conn # conn
# |> get("/users/#{user.nickname}/following") # |> get("/@#{actor.preferred_username}/following")
# |> json_response(200) # |> json_response(200)
#
# assert length(result["first"]["orderedItems"]) == 10 # assert length(result["first"]["orderedItems"]) == 10
# assert result["first"]["totalItems"] == 15 # assert result["first"]["totalItems"] == 15
# assert result["totalItems"] == 15 # assert result["totalItems"] == 15
#
# result = # result =
# conn # conn
# |> get("/users/#{user.nickname}/following?page=2") # |> get("/@#{actor.preferred_username}/following?page=2")
# |> json_response(200) # |> json_response(200)
#
# assert length(result["orderedItems"]) == 5 # assert length(result["orderedItems"]) == 5
# assert result["totalItems"] == 15 # assert result["totalItems"] == 15
# end # end

View File

@ -31,6 +31,64 @@ defmodule EventosWeb.ActorControllerTest do
end end
end end
describe "show actor" do
test "show existing actor", %{conn: conn, actor: actor} do
actor_id = actor.id
conn = get(conn, actor_path(conn, :show, actor.preferred_username))
assert %{"data" => %{"id" => actor_id}} = json_response(conn, 200)
end
test "show non-existing actor", %{conn: conn, actor: actor} do
actor_id = actor.id
conn = get(conn, actor_path(conn, :show, "nonexisting"))
assert "" == response(conn, 404)
end
end
describe "search for actors" do
test "search for existing actors", %{conn: conn, actor: actor} do
actor_username = actor.preferred_username
conn = get(conn, actor_path(conn, :search, actor_username))
assert %{"data" => [%{"username" => actor_username}]} = json_response(conn, 200)
end
test "search for existing actors with similar username", %{conn: conn, actor: actor} do
actor_username = actor.preferred_username
conn = get(conn, actor_path(conn, :search, "thom"))
assert %{"data" => [%{"username" => actor_username}]} = json_response(conn, 200)
end
test "search for nothing", %{conn: conn, actor: actor} do
actor_username = actor.preferred_username
conn = get(conn, actor_path(conn, :search, "nothing"))
assert %{"data" => []} = json_response(conn, 200)
end
end
describe "update actor" do
test "update actor with valid attrs", %{conn: conn, user: user, actor: actor} do
conn = auth_conn(conn, user)
conn =
patch(conn, actor_path(conn, :update, actor.preferred_username), %{
"actor" => %{"name" => "glouglou"}
})
assert %{"data" => %{"display_name" => "glouglou"}} = json_response(conn, 200)
end
test "update actor with invalid attrs", %{conn: conn, user: user, actor: actor} do
conn = auth_conn(conn, user)
conn =
patch(conn, actor_path(conn, :update, actor.preferred_username), %{
"actor" => %{"preferred_username" => nil}
})
assert json_response(conn, 422)["errors"] != %{}
end
end
### ###
# Not possible atm # Not possible atm
### ###

View File

@ -34,7 +34,7 @@ defmodule EventosWeb.AddressControllerTest do
floor: nil, floor: nil,
postalCode: nil, postalCode: nil,
streetAddress: nil, streetAddress: nil,
geom: %{type: nil, data: %{latitude: nil, longitude: nil}} geom: %{type: "oh no", data: %{latitude: nil, longitude: nil}}
} }
def fixture(:address) do def fixture(:address) do

View File

@ -5,6 +5,7 @@ defmodule EventosWeb.UserControllerTest do
alias Eventos.Actors alias Eventos.Actors
alias Eventos.Actors.User alias Eventos.Actors.User
use Bamboo.Test
@create_attrs %{email: "foo@bar.tld", password: "some password_hash", username: "some username"} @create_attrs %{email: "foo@bar.tld", password: "some password_hash", username: "some username"}
# @update_attrs %{email: "foo@fighters.tld", password: "some updated password_hash", username: "some updated username"} # @update_attrs %{email: "foo@fighters.tld", password: "some updated password_hash", username: "some updated username"}
@ -18,7 +19,7 @@ defmodule EventosWeb.UserControllerTest do
setup %{conn: conn} do setup %{conn: conn} do
user = insert(:user) user = insert(:user)
actor = insert(:actor, user: user) actor = insert(:actor, user: user)
{:ok, conn: conn, user: user} {:ok, conn: conn, user: user, actor: actor}
end end
describe "index" do describe "index" do
@ -31,12 +32,14 @@ defmodule EventosWeb.UserControllerTest do
describe "create user" do describe "create user" do
test "renders user when data is valid", %{conn: conn} do test "renders user when data is valid", %{conn: conn} do
conn = post(conn, user_path(conn, :create), @create_attrs) conn = post(conn, user_path(conn, :register), @create_attrs)
assert %{"email" => "foo@bar.tld"} = json_response(conn, 201) assert %{"email" => "foo@bar.tld"} = json_response(conn, 201)
assert {:ok, %User{} = user} = Eventos.Actors.get_user_by_email(@create_attrs.email)
assert_delivered_email(Eventos.Email.User.confirmation_email(user))
end end
test "renders errors when data is invalid", %{conn: conn} do test "renders errors when data is invalid", %{conn: conn} do
conn = post(conn, user_path(conn, :create), @invalid_attrs) conn = post(conn, user_path(conn, :register), @invalid_attrs)
assert json_response(conn, 422)["errors"] != %{} assert json_response(conn, 422)["errors"] != %{}
end end
@ -47,11 +50,139 @@ defmodule EventosWeb.UserControllerTest do
username: "framasoft" username: "framasoft"
} }
conn = post(conn, user_path(conn, :create), attrs) conn = post(conn, user_path(conn, :register), attrs)
assert %{"email" => "contact@framasoft.org"} = json_response(conn, 201) assert %{"email" => "contact@framasoft.org"} = json_response(conn, 201)
end end
end end
describe "validating user" do
test "validate user when token is valid", %{conn: conn} do
conn = post(conn, user_path(conn, :create), @create_attrs)
assert %{"email" => "foo@bar.tld"} = json_response(conn, 201)
assert {:ok, %User{} = user} = Eventos.Actors.get_user_by_email(@create_attrs.email)
assert_delivered_email(Eventos.Email.User.confirmation_email(user))
conn = get(conn, user_path(conn, :validate, user.confirmation_token))
assert %{"user" => _, "token" => _} = json_response(conn, 200)
end
test "validate user when token is invalid", %{conn: conn} do
conn = post(conn, user_path(conn, :create), @create_attrs)
assert %{"email" => "foo@bar.tld"} = json_response(conn, 201)
assert {:ok, %User{} = user} = Eventos.Actors.get_user_by_email(@create_attrs.email)
assert_delivered_email(Eventos.Email.User.confirmation_email(user))
conn = get(conn, user_path(conn, :validate, "toto"))
assert %{"error" => _} = json_response(conn, 404)
end
end
describe "revalidating user" do
test "ask to resend token to user when too soon", %{conn: conn} do
conn = post(conn, user_path(conn, :create), @create_attrs)
assert %{"email" => "foo@bar.tld"} = json_response(conn, 201)
assert {:ok, %User{} = user} = Eventos.Actors.get_user_by_email(@create_attrs.email)
assert_delivered_email(Eventos.Email.User.confirmation_email(user))
conn = post(conn, user_path(conn, :resend_confirmation), %{"email" => @create_attrs.email})
assert %{"error" => _} = json_response(conn, 404)
end
test "ask to resend token to user when the time is right", %{conn: conn} do
conn = post(conn, user_path(conn, :create), @create_attrs)
assert %{"email" => "foo@bar.tld"} = json_response(conn, 201)
assert {:ok, %User{} = user} = Eventos.Actors.get_user_by_email(@create_attrs.email)
assert_delivered_email(Eventos.Email.User.confirmation_email(user))
# Hammer time !
{:ok, %User{} = user} =
Eventos.Actors.update_user(user, %{
confirmation_sent_at: Timex.shift(user.confirmation_sent_at, hours: -3)
})
conn = post(conn, user_path(conn, :resend_confirmation), %{"email" => @create_attrs.email})
assert_delivered_email(Eventos.Email.User.confirmation_email(user))
assert %{"email" => "foo@bar.tld"} = json_response(conn, 200)
end
end
describe "resetting user's password" do
test "ask for reset", %{conn: conn, user: user} do
user_email = user.email
# Send reset email
conn = post(conn, user_path(conn, :send_reset_password), %{"email" => user_email})
assert {:ok, %User{} = user} = Eventos.Actors.get_user_by_email(user.email)
assert_delivered_email(Eventos.Email.User.reset_password_email(user))
assert %{"email" => user_email} = json_response(conn, 200)
# Call reset route
conn =
post(conn, user_path(conn, :reset_password), %{
"password" => "new password",
"token" => user.reset_password_token
})
user_id = user.id
assert %{"user" => %{"id" => user_id}} = json_response(conn, 200)
end
test "ask twice for reset too soon", %{conn: conn, user: user} do
user_email = user.email
# Send reset email
conn = post(conn, user_path(conn, :send_reset_password), %{"email" => user.email})
assert {:ok, %User{} = user} = Eventos.Actors.get_user_by_email(user.email)
assert_delivered_email(Eventos.Email.User.reset_password_email(user))
assert %{"email" => user_email} = json_response(conn, 200)
# Send reset email again
conn = post(conn, user_path(conn, :send_reset_password), %{"email" => user.email})
assert %{"errors" => "You requested a new reset password too early"} =
json_response(conn, 404)
end
test "ask twice for reset after a while", %{conn: conn, user: user} do
user_email = user.email
# Send reset email
conn = post(conn, user_path(conn, :send_reset_password), %{"email" => user.email})
assert {:ok, %User{} = user} = Eventos.Actors.get_user_by_email(user.email)
assert_delivered_email(Eventos.Email.User.reset_password_email(user))
assert %{"email" => user_email} = json_response(conn, 200)
# Hammer time !
{:ok, %User{} = user} =
Eventos.Actors.update_user(user, %{
reset_password_sent_at: Timex.shift(user.reset_password_sent_at, hours: -3)
})
# Send reset email again
conn = post(conn, user_path(conn, :send_reset_password), %{"email" => user.email})
assert {:ok, %User{} = user} = Eventos.Actors.get_user_by_email(user.email)
assert_delivered_email(Eventos.Email.User.reset_password_email(user))
assert %{"email" => user_email} = json_response(conn, 200)
end
test "ask for reset with wrong address", %{conn: conn} do
conn = post(conn, user_path(conn, :send_reset_password), %{"email" => "yolo@coucou"})
assert %{"errors" => "Unable to find an user with this email"} = json_response(conn, 404)
end
test "calling reset route with wrong token", %{conn: conn} do
conn =
post(conn, user_path(conn, :reset_password), %{
"password" => "new password",
"token" => "just wrong"
})
assert %{"errors" => %{"token" => ["Wrong token for password reset"]}} =
json_response(conn, 404)
end
end
# describe "update user" do # describe "update user" do
# setup [:create_user] # setup [:create_user]
# #

View File

@ -0,0 +1,65 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"Emoji": "toot:Emoji",
"Hashtag": "as:Hashtag",
"atomUri": "ostatus:atomUri",
"conversation": "ostatus:conversation",
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"movedTo": "as:movedTo",
"ostatus": "http://ostatus.org#",
"sensitive": "as:sensitive",
"toot": "http://joinmastodon.org/ns#"
}
],
"actor": "http://framapiaf.org/users/admin",
"cc": [
"http://framapiaf.org/users/admin/followers",
"http://eventos.com/@tcit"
],
"id": "http://framapiaf.org/users/admin/statuses/99512778738411822/activity",
"nickname": "lain",
"object": {
"atomUri": "http://framapiaf.org/users/admin/statuses/99512778738411822",
"attachment": [],
"attributedTo": "http://framapiaf.org/users/admin",
"cc": [
"http://framapiaf.org/users/admin/followers",
"http://localtesting.pleroma.lol/users/lain"
],
"content": "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>",
"conversation": "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation",
"id": "http://framapiaf.org/users/admin/statuses/99512778738411822",
"inReplyTo": null,
"inReplyToAtomUri": null,
"published": "2018-02-12T14:08:20Z",
"sensitive": true,
"summary": "cw",
"tag": [
{
"href": "http://localtesting.pleroma.lol/users/lain",
"name": "@lain@localtesting.pleroma.lol",
"type": "Mention"
}
],
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"type": "Note",
"url": "http://framapiaf.org/@admin/99512778738411822"
},
"published": "2018-02-12T14:08:20Z",
"signature": {
"created": "2018-02-12T14:08:20Z",
"creator": "http://framapiaf.org/users/admin#main-key",
"signatureValue": "rnNfcopkc6+Ju73P806popcfwrK9wGYHaJVG1/ZvrlEbWVDzaHjkXqj9Q3/xju5l8CSn9tvSgCCtPFqZsFQwn/pFIFUcw7ZWB2xi4bDm3NZ3S4XQ8JRaaX7og5hFxAhWkGhJhAkfxVnOg2hG+w2d/7d7vRVSC1vo5ip4erUaA/PkWusZvPIpxnRWoXaxJsFmVx0gJgjpJkYDyjaXUlp+jmaoseeZ4EPQUWqHLKJ59PRG0mg8j2xAjYH9nQaN14qMRmTGPxY8gfv/CUFcatA+8VJU9KEsJkDAwLVvglydNTLGrxpAJU78a2eaht0foV43XUIZGe3DKiJPgE+UOKGCJw==",
"type": "RsaSignature2017"
},
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"type": "Create"
}

View File

@ -67,11 +67,14 @@ defmodule Eventos.Factory do
end end
def comment_factory do def comment_factory do
uuid = Ecto.UUID.generate()
%Eventos.Events.Comment{ %Eventos.Events.Comment{
text: "My Comment", text: "My Comment",
actor: build(:actor), actor: build(:actor),
event: build(:event), event: build(:event),
uuid: Ecto.UUID.generate() uuid: uuid,
url: "#{EventosWeb.Endpoint.url()}/comments/#{uuid}"
} }
end end
@ -86,10 +89,18 @@ defmodule Eventos.Factory do
organizer_actor: actor, organizer_actor: actor,
category: build(:category), category: build(:category),
physical_address: build(:address), physical_address: build(:address),
public: true,
url: "#{EventosWeb.Endpoint.url()}/@#{actor.url}/#{Ecto.UUID.generate()}" url: "#{EventosWeb.Endpoint.url()}/@#{actor.url}/#{Ecto.UUID.generate()}"
} }
end end
def participant_factory do
%Eventos.Events.Participant{
event: build(:event),
actor: build(:actor)
}
end
def session_factory do def session_factory do
%Eventos.Events.Session{ %Eventos.Events.Session{
title: sequence("MySession"), title: sequence("MySession"),