Allow tag relations + bump ecto deps

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
master
Thomas Citharel 4 years ago
parent 4caa998ae0
commit 256d50e855
No known key found for this signature in database
GPG Key ID: A061B9DDE0CA0773
  1. 1
      config/dev.exs
  2. 1
      config/prod.exs
  3. 1
      config/test.exs
  4. 2
      lib/mobilizon/actors/actors.ex
  5. 7
      lib/mobilizon/actors/service/activation.ex
  6. 2
      lib/mobilizon/actors/service/reset_password.ex
  7. 5
      lib/mobilizon/actors/service/tools.ex
  8. 9
      lib/mobilizon/actors/user.ex
  9. 6
      lib/mobilizon/events/event.ex
  10. 107
      lib/mobilizon/events/events.ex
  11. 4
      lib/mobilizon/events/session.ex
  12. 2
      lib/mobilizon/events/tag.ex
  13. 41
      lib/mobilizon/events/tag_relations.ex
  14. 2
      lib/mobilizon/postgrex_types.ex
  15. 4
      lib/mobilizon/repo.ex
  16. 2
      lib/mobilizon_web/auth_error_handler.ex
  17. 2
      lib/mobilizon_web/endpoint.ex
  18. 40
      lib/mobilizon_web/resolvers/tag.ex
  19. 8
      lib/mobilizon_web/schema/event.ex
  20. 30
      lib/mobilizon_web/schema/tag.ex
  21. 5
      lib/service/activity_pub/activity_pub.ex
  22. 2
      lib/service/activity_pub/utils.ex
  23. 2
      lib/service/federator.ex
  24. 17
      mix.exs
  25. 42
      mix.lock
  26. 22
      priv/repo/migrations/20190208141700_tags_relations.exs
  27. 48
      priv/repo/migrations/20190214100858_drop_datetimetz.exs
  28. 24
      priv/repo/seeds.exs
  29. 9
      test/fixtures/mastodon-delete.json
  30. 2
      test/mobilizon/actors/actors_test.exs
  31. 95
      test/mobilizon/events/events_test.exs
  32. 80
      test/mobilizon/service/activity_pub/transmogrifier_test.exs
  33. 2
      test/mobilizon_web/controllers/activity_pub_controller_test.exs
  34. 6
      test/mobilizon_web/resolvers/event_resolver_test.exs
  35. 2
      test/mobilizon_web/resolvers/participant_resolver_test.exs
  36. 4
      test/mobilizon_web/resolvers/user_resolver_test.exs
  37. 9
      test/support/factory.ex

@ -57,7 +57,6 @@ config :mobilizon, Mobilizon.Mailer, adapter: Bamboo.LocalAdapter
# Configure your database
config :mobilizon, Mobilizon.Repo,
adapter: Ecto.Adapters.Postgres,
types: Mobilizon.PostgresTypes,
username: System.get_env("MOBILIZON_DATABASE_USERNAME") || "mobilizon",
password: System.get_env("MOBILIZON_DATABASE_PASSWORD") || "mobilizon",

@ -28,7 +28,6 @@ config :mobilizon, MobilizonWeb.Endpoint,
# Configure your database
config :mobilizon, Mobilizon.Repo,
adapter: Ecto.Adapters.Postgres,
types: Mobilizon.PostgresTypes,
username: System.get_env("MOBILIZON_DATABASE_USERNAME") || "mobilizon",
password: System.get_env("MOBILIZON_DATABASE_PASSWORD") || "mobilizon",

@ -20,7 +20,6 @@ config :logger,
# Configure your database
config :mobilizon, Mobilizon.Repo,
adapter: Ecto.Adapters.Postgres,
types: Mobilizon.PostgresTypes,
username: System.get_env("MOBILIZON_DATABASE_USERNAME") || "mobilizon",
password: System.get_env("MOBILIZON_DATABASE_PASSWORD") || "mobilizon",

@ -579,7 +579,7 @@ defmodule Mobilizon.Actors do
"""
def authenticate(%{user: user, password: password}) do
# Does password match the one stored in the database?
case Comeonin.Argon2.checkpw(password, user.password_hash) do
case Argon2.verify_pass(password, user.password_hash) do
true ->
# Yes, create and return the token
MobilizonWeb.Guardian.encode_and_sign(user)

@ -12,7 +12,7 @@ defmodule Mobilizon.Actors.Service.Activation do
with %User{} = user <- Actors.get_user_by_activation_token(token),
{:ok, %User{} = user} <-
Actors.update_user(user, %{
"confirmed_at" => DateTime.utc_now(),
"confirmed_at" => DateTime.utc_now() |> DateTime.truncate(:second),
"confirmation_sent_at" => nil,
"confirmation_token" => nil
}) do
@ -26,7 +26,10 @@ defmodule Mobilizon.Actors.Service.Activation do
def resend_confirmation_email(%User{} = user, locale \\ "en") do
with :ok <- Tools.we_can_send_email(user, :confirmation_sent_at),
{:ok, user} <- Actors.update_user(user, %{"confirmation_sent_at" => DateTime.utc_now()}) do
{:ok, user} <-
Actors.update_user(user, %{
"confirmation_sent_at" => DateTime.utc_now() |> DateTime.truncate(:second)
}) do
send_confirmation_email(user, locale)
Logger.info("Sent confirmation email again to #{user.email}")
{:ok, user.email}

@ -43,7 +43,7 @@ defmodule Mobilizon.Actors.Service.ResetPassword do
Repo.update(
User.send_password_reset_changeset(user, %{
"reset_password_token" => Tools.random_string(30),
"reset_password_sent_at" => DateTime.utc_now()
"reset_password_sent_at" => DateTime.utc_now() |> DateTime.truncate(:second)
})
) do
mail =

@ -11,7 +11,10 @@ defmodule Mobilizon.Actors.Service.Tools do
:ok
_ ->
case Timex.before?(Timex.shift(Map.get(user, key), hours: 1), DateTime.utc_now()) do
case Timex.before?(
Timex.shift(Map.get(user, key), hours: 1),
DateTime.utc_now() |> DateTime.truncate(:second)
) do
true ->
:ok

@ -89,7 +89,12 @@ defmodule Mobilizon.Actors.User do
case changeset do
%Ecto.Changeset{valid?: true, changes: %{email: _email}} ->
changeset = put_change(changeset, :confirmation_token, random_string(30))
put_change(changeset, :confirmation_sent_at, DateTime.utc_now())
put_change(
changeset,
:confirmation_sent_at,
DateTime.utc_now() |> DateTime.truncate(:second)
)
_ ->
changeset
@ -122,7 +127,7 @@ defmodule Mobilizon.Actors.User do
put_change(
changeset,
:password_hash,
Comeonin.Argon2.hashpwsalt(password)
Argon2.hash_pwd_salt(password)
)
_ ->

@ -32,16 +32,16 @@ defmodule Mobilizon.Events.Event do
schema "events" do
field(:url, :string)
field(:local, :boolean, default: true)
field(:begins_on, Timex.Ecto.DateTimeWithTimezone)
field(:begins_on, :utc_datetime)
field(:description, :string)
field(:ends_on, Timex.Ecto.DateTimeWithTimezone)
field(:ends_on, :utc_datetime)
field(:title, :string)
field(:status, Mobilizon.Events.EventStatusEnum, default: :confirmed)
field(:visibility, Mobilizon.Events.EventVisibilityEnum, default: :public)
field(:join_options, Mobilizon.Events.JoinOptionsEnum, default: :free)
field(:thumbnail, :string)
field(:large_image, :string)
field(:publish_at, Timex.Ecto.DateTimeWithTimezone)
field(:publish_at, :utc_datetime)
field(:uuid, Ecto.UUID, default: Ecto.UUID.generate())
field(:online_address, :string)
field(:phone_address, :string)

@ -458,8 +458,32 @@ defmodule Mobilizon.Events do
[%Tag{}, ...]
"""
def list_tags do
Repo.all(Tag)
def list_tags(page \\ nil, limit \\ nil) do
Repo.all(
Tag
|> paginate(page, limit)
)
end
@doc """
Returns the list of tags for an event.
## Examples
iex> list_tags_for_event(id)
[%Participant{}, ...]
"""
def list_tags_for_event(id, page \\ nil, limit \\ nil) do
Repo.all(
from(
t in Tag,
join: e in "events_tags",
on: t.id == e.tag_id,
where: e.event_id == ^id
)
|> paginate(page, limit)
)
end
@doc """
@ -543,6 +567,85 @@ defmodule Mobilizon.Events do
Tag.changeset(tag, %{})
end
alias Mobilizon.Events.TagRelation
@doc """
Create a relation between two tags
"""
def create_tag_relation(attrs \\ {}) do
%TagRelation{}
|> TagRelation.changeset(attrs)
|> Repo.insert(conflict_target: [:tag_id, :link_id], on_conflict: [inc: [weight: 1]])
end
@doc """
Remove a tag relation
"""
def delete_tag_relation(%TagRelation{} = tag_relation) do
Repo.delete(tag_relation)
end
@doc """
Returns whether two tags are linked or not
"""
def are_tags_linked(%Tag{id: tag1_id}, %Tag{id: tag2_id}) do
case from(tr in TagRelation,
where: tr.tag_id == ^min(tag1_id, tag2_id) and tr.link_id == ^max(tag1_id, tag2_id)
)
|> Repo.one() do
%TagRelation{} -> true
_ -> false
end
end
@doc """
Returns the tags neighbors for a given tag
We can't rely on the single many_to_many relation since we also want tags that link to our tag, not just tags linked by this one
The SQL query looks like this:
```sql
SELECT * FROM tags t
RIGHT JOIN (
SELECT weight, link_id AS id
FROM tag_relations t2
WHERE tag_id = 1
UNION ALL
SELECT tag_id AS id, weight
FROM tag_relations t2
WHERE link_id = 1
) tr
ON t.id = tr.id
ORDER BY tr.weight
DESC;
```
"""
def tag_neighbors(%Tag{id: id}, relation_minimum \\ 1, limit \\ 10) do
query2 =
from(tr in TagRelation,
select: %{id: tr.tag_id, weight: tr.weight},
where: tr.link_id == ^id
)
query =
from(tr in TagRelation,
select: %{id: tr.link_id, weight: tr.weight},
union_all: ^query2,
where: tr.tag_id == ^id
)
final_query =
from(t in Tag,
right_join: q in subquery(query),
on: [id: t.id],
where: q.weight >= ^relation_minimum,
limit: ^limit,
order_by: [desc: q.weight]
)
Repo.all(final_query)
end
alias Mobilizon.Events.Participant
@doc """

@ -15,8 +15,8 @@ defmodule Mobilizon.Events.Session do
field(:subtitle, :string)
field(:title, :string)
field(:videos_urls, :string)
field(:begins_on, Timex.Ecto.DateTimeWithTimezone)
field(:ends_on, Timex.Ecto.DateTimeWithTimezone)
field(:begins_on, :utc_datetime)
field(:ends_on, :utc_datetime)
belongs_to(:event, Event)
belongs_to(:track, Track)

@ -39,10 +39,12 @@ defmodule Mobilizon.Events.Tag do
import Ecto.Changeset
alias Mobilizon.Events.Tag
alias Mobilizon.Events.Tag.TitleSlug
alias Mobilizon.Events.TagRelation
schema "tags" do
field(:title, :string)
field(:slug, TitleSlug.Type)
many_to_many(:related_tags, Tag, join_through: TagRelation)
timestamps()
end

@ -0,0 +1,41 @@
defmodule Mobilizon.Events.TagRelation do
@moduledoc """
Represents a tag for events
"""
use Ecto.Schema
import Ecto.Changeset
alias Mobilizon.Events.Tag
alias Mobilizon.Events.TagRelation
@primary_key false
schema "tag_relations" do
belongs_to(:tag, Tag, primary_key: true)
belongs_to(:link, Tag, primary_key: true)
field(:weight, :integer, default: 1)
end
@doc false
def changeset(%TagRelation{} = tag, attrs) do
changeset =
tag
|> cast(attrs, [:tag_id, :link_id, :weight])
|> validate_required([:tag_id, :link_id])
# Return if tag_id or link_id are not set because it will fail later otherwise
with %Ecto.Changeset{errors: []} <- changeset do
changes = changeset.changes
changeset =
changeset
|> put_change(:tag_id, min(changes.tag_id, changes.link_id))
|> put_change(:link_id, max(changes.tag_id, changes.link_id))
changeset
|> unique_constraint(:tag_id, name: :tag_relations_pkey)
|> check_constraint(:tag_id,
name: :no_self_loops_check,
message: "Can't add a relation on self"
)
end
end
end

@ -1,5 +1,5 @@
Postgrex.Types.define(
Mobilizon.PostgresTypes,
[Geo.PostGIS.Extension] ++ Ecto.Adapters.Postgres.extensions(),
json: Poison
json: Jason
)

@ -2,7 +2,9 @@ defmodule Mobilizon.Repo do
@moduledoc """
Mobilizon Repo
"""
use Ecto.Repo, otp_app: :mobilizon
use Ecto.Repo,
otp_app: :mobilizon,
adapter: Ecto.Adapters.Postgres
@doc """
Dynamically loads the repository url from the

@ -5,7 +5,7 @@ defmodule MobilizonWeb.AuthErrorHandler do
import Plug.Conn
def auth_error(conn, {type, _reason}, _opts) do
body = Poison.encode!(%{message: to_string(type)})
body = Jason.encode!(%{message: to_string(type)})
send_resp(conn, 401, body)
end
end

@ -40,7 +40,7 @@ defmodule MobilizonWeb.Endpoint do
Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Poison
json_decoder: Jason
)
plug(Plug.MethodOverride)

@ -0,0 +1,40 @@
defmodule MobilizonWeb.Resolvers.Tag do
@moduledoc """
Handles the tag-related GraphQL calls
"""
require Logger
alias Mobilizon.Events.Event
alias Mobilizon.Events.Tag
def list_tags(_parent, %{page: page, limit: limit}, _resolution) do
tags = Mobilizon.Events.list_tags(page, limit)
{:ok, tags}
end
@doc """
Retrieve the list of tags for an event
"""
def list_tags_for_event(%Event{id: id}, _args, _resolution) do
{:ok, Mobilizon.Events.list_tags_for_event(id)}
end
@doc """
Retrieve the list of related tags for a given tag ID
"""
def get_related_tags(_parent, %{tag_id: tag_id}, _resolution) do
with %Tag{} = tag <- Mobilizon.Events.get_tag!(tag_id),
tags <- Mobilizon.Events.tag_neighbors(tag) do
{:ok, tags}
end
end
@doc """
Retrieve the list of related tags for a parent tag
"""
def get_related_tags(%Tag{} = tag, _args, _resolution) do
with tags <- Mobilizon.Events.tag_neighbors(tag) do
{:ok, tags}
end
end
end

@ -8,6 +8,7 @@ defmodule MobilizonWeb.Schema.EventType do
import_types(MobilizonWeb.Schema.AddressType)
import_types(MobilizonWeb.Schema.Events.ParticipantType)
import_types(MobilizonWeb.Schema.Events.CategoryType)
import_types(MobilizonWeb.Schema.TagType)
alias MobilizonWeb.Resolvers
@desc "An event"
@ -37,7 +38,12 @@ defmodule MobilizonWeb.Schema.EventType do
)
field(:attributed_to, :actor, description: "Who the event is attributed to (often a group)")
# field(:tags, list_of(:tag))
field(:tags, list_of(:tag),
resolve: &MobilizonWeb.Resolvers.Tag.list_tags_for_event/3,
description: "The event's tags"
)
field(:category, :category, description: "The event's category")
field(:participants, list_of(:participant),

@ -0,0 +1,30 @@
defmodule MobilizonWeb.Schema.TagType do
@moduledoc """
Schema representation for Tags
"""
use Absinthe.Schema.Notation
alias MobilizonWeb.Resolvers
@desc "A tag"
object :tag do
field(:id, :id, description: "The tag's ID")
field(:slug, :string, description: "The tags's slug")
field(:title, :string, description: "The tag's title")
field(
:related,
list_of(:tag),
resolve: &Resolvers.Tag.get_related_tags/3,
description: "Related tags to this tag"
)
end
object :tag_queries do
@desc "Get the list of tags"
field :tags, non_null(list_of(:tag)) do
arg(:page, :integer, default_value: 1)
arg(:limit, :integer, default_value: 10)
resolve(&Resolvers.Tag.list_tags/3)
end
end
end

@ -495,7 +495,10 @@ defmodule Mobilizon.Service.ActivityPub do
ical_events =
body
|> ExIcal.parse()
|> ExIcal.by_range(DateTime.utc_now(), DateTime.utc_now() |> Timex.shift(years: 1))
|> ExIcal.by_range(
DateTime.utc_now(),
DateTime.utc_now() |> DateTime.truncate(:second) |> Timex.shift(years: 1)
)
activities =
ical_events

@ -46,7 +46,7 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
end
def make_date do
DateTime.utc_now() |> DateTime.to_iso8601()
DateTime.utc_now() |> DateTime.truncate(:second) |> DateTime.to_iso8601()
end
@doc """

@ -59,7 +59,7 @@ defmodule Mobilizon.Service.Federator do
_e ->
# Just drop those for now
Logger.error("Unhandled activity")
Logger.error(Poison.encode!(params, pretty: 2))
Logger.error(Jason.encode!(params))
end
end

@ -51,27 +51,25 @@ defmodule Mobilizon.Mixfile do
[
{:phoenix, "~> 1.4.0"},
{:phoenix_pubsub, "~> 1.0"},
{:phoenix_ecto, "~> 3.2"},
{:phoenix_ecto, "~> 4.0"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 2.10"},
{:gettext, "~> 0.11"},
{:cowboy, "~> 1.0"},
{:cowboy, "~> 2.6"},
{:guardian, "~> 1.2"},
{:guardian_db, "~> 1.1"},
{:comeonin, "~> 4.0"},
{:argon2_elixir, "~> 1.2"},
{:guardian_db, "~> 2.0"},
{:argon2_elixir, "~> 2.0"},
{:cors_plug, "~> 2.0"},
{:ecto_autoslug_field, "~> 1.0"},
{:rsa_ex, "~> 0.1"},
{:geo, "~> 3.0"},
{:geo_postgis, "~> 2.0"},
{:geo_postgis, "~> 3.1"},
{:timex, "~> 3.0"},
{:timex_ecto, "~> 3.0"},
{:icalendar, "~> 0.6"},
{:exgravatar, "~> 2.0.1"},
{:httpoison, "~> 1.0"},
{:json_ld, "~> 0.3"},
{:jason, "~> 1.0"},
{:jason, "~> 1.1"},
{:ex_crypto, "~> 0.9.0"},
{:http_sign, "~> 0.1.1"},
{:ecto_enum, "~> 1.0"},
@ -82,12 +80,11 @@ defmodule Mobilizon.Mixfile do
{:absinthe, "~> 1.4.0"},
{:absinthe_phoenix, "~> 1.4.0"},
{:absinthe_plug, "~> 1.4.0"},
{:poison, "~> 3.1"},
{:absinthe_ecto, "~> 0.1.3"},
{:dataloader, "~> 1.0"},
{:arc, "~> 0.11.0"},
{:arc_ecto, "~> 0.11.0"},
{:plug_cowboy, "~> 1.0"},
{:plug_cowboy, "~> 2.0"},
# Dev and test dependencies
{:phoenix_live_reload, "~> 1.2", only: :dev},
{:ex_machina, "~> 2.2", only: [:dev, :test]},

@ -5,30 +5,30 @@
"absinthe_plug": {:hex, :absinthe_plug, "1.4.6", "ac5d2d3d02acf52fda0f151b294017ab06e2ed1c6c15334e06aac82c94e36e08", [:mix], [{:absinthe, "~> 1.4.11", [hex: :absinthe, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.2 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"arc": {:hex, :arc, "0.11.0", "ac7a0cc03035317b6fef9fe94c97d7d9bd183a3e7ce1606aa0c175cfa8d1ba6d", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:ex_aws_s3, "~> 2.0", [hex: :ex_aws_s3, repo: "hexpm", optional: true]}, {:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm"},
"arc_ecto": {:hex, :arc_ecto, "0.11.1", "27aedf8c236b2097eed09d96f4ae73b43eb4c042a0e2ae42d44bf644cf16115c", [:mix], [{:arc, "~> 0.11.0", [hex: :arc, repo: "hexpm", optional: false]}, {:ecto, "~> 2.1 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm"},
"argon2_elixir": {:hex, :argon2_elixir, "1.3.3", "487ffa071ef78c51d9b16e50ff3cf30cf8204e0aa4bdc8afd3765fdd8195e213", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
"argon2_elixir": {:hex, :argon2_elixir, "2.0.0", "e3539f441930d4c8296e36024168526626351c1f2c2df97cfd50f4e90b15386a", [:make, :mix], [{:comeonin, "~> 5.0", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
"bamboo": {:hex, :bamboo, "1.2.0", "8aebd24f7c606c32d0163c398004a11608ca1028182a169b2e527793bfab7561", [:mix], [{:hackney, ">= 1.13.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"},
"bamboo_smtp": {:hex, :bamboo_smtp, "1.6.0", "0a3607b77f22554af58c547350c1c73ebba6f4fb2c4bd0b11713ab5b4081588f", [:mix], [{:bamboo, "~> 1.0", [hex: :bamboo, repo: "hexpm", optional: false]}, {:gen_smtp, "~> 0.12.0", [hex: :gen_smtp, repo: "hexpm", optional: false]}], "hexpm"},
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
"certifi": {:hex, :certifi, "2.4.2", "75424ff0f3baaccfd34b1214184b6ef616d89e420b258bb0a5ea7d7bc628f7f0", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm"},
"comeonin": {:hex, :comeonin, "4.1.2", "3eb5620fd8e35508991664b4c2b04dd41e52f1620b36957be837c1d7784b7592", [:mix], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, repo: "hexpm", optional: true]}], "hexpm"},
"comeonin": {:hex, :comeonin, "5.0.0", "e87716d3b1c31e56312f6a1545a5548cdc80376cff5025fe3b12be2046934837", [:mix], [], "hexpm"},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
"cors_plug": {:hex, :cors_plug, "2.0.0", "238ddb479f92b38f6dc1ae44b8d81f0387f9519101a6da442d543ab70ee0e482", [:mix], [{:plug, "~> 1.3 or ~> 1.4 or ~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm"},
"cowboy": {:hex, :cowboy, "2.6.1", "f2e06f757c337b3b311f9437e6e072b678fcd71545a7b2865bdaa154d078593f", [:rebar3], [{:cowlib, "~> 2.7.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
"cowlib": {:hex, :cowlib, "2.7.0", "3ef16e77562f9855a2605900cedb15c1462d76fb1be6a32fc3ae91973ee543d2", [:rebar3], [], "hexpm"},
"credo": {:hex, :credo, "1.0.2", "88bc918f215168bf6ce7070610a6173c45c82f32baa08bdfc80bf58df2d103b6", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
"dataloader": {:hex, :dataloader, "1.0.6", "fb724d6d3fb6acb87d27e3b32dea3a307936ad2d245faf9cf5221d1323d6a4ba", [:mix], [{:ecto, ">= 0.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
"db_connection": {:hex, :db_connection, "1.1.3", "89b30ca1ef0a3b469b1c779579590688561d586694a3ce8792985d4d7e575a61", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
"decimal": {:hex, :decimal, "1.6.0", "bfd84d90ff966e1f5d4370bdd3943432d8f65f07d3bab48001aebd7030590dcc", [:mix], [], "hexpm"},
"db_connection": {:hex, :db_connection, "2.0.5", "ddb2ba6761a08b2bb9ca0e7d260e8f4dd39067426d835c24491a321b7f92a4da", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
"decimal": {:hex, :decimal, "1.7.0", "30d6b52c88541f9a66637359ddf85016df9eb266170d53105f02e4a67e00c5aa", [:mix], [], "hexpm"},
"dialyxir": {:hex, :dialyxir, "1.0.0-rc.4", "71b42f5ee1b7628f3e3a6565f4617dfb02d127a0499ab3e72750455e986df001", [:mix], [{:erlex, "~> 0.1", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm"},
"earmark": {:hex, :earmark, "1.3.1", "73812f447f7a42358d3ba79283cfa3075a7580a3a2ed457616d6517ac3738cb9", [:mix], [], "hexpm"},
"ecto": {:hex, :ecto, "2.2.11", "4bb8f11718b72ba97a2696f65d247a379e739a0ecabf6a13ad1face79844791c", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
"ecto": {:hex, :ecto, "3.0.7", "44dda84ac6b17bbbdeb8ac5dfef08b7da253b37a453c34ab1a98de7f7e5fec7f", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"},
"ecto_autoslug_field": {:hex, :ecto_autoslug_field, "1.0.0", "577eed25e6d045b8d783f82c9872f97c3a84017a4feae50eaf3cf4e1334a19e2", [:mix], [{:ecto, ">= 2.1.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:slugger, ">= 0.2.0", [hex: :slugger, repo: "hexpm", optional: false]}], "hexpm"},
"ecto_enum": {:hex, :ecto_enum, "1.2.0", "9ead3ee04efc4cb68a50560a9d9ebb665dd697f957f1c3df8e81bf863cf7a4e9", [:mix], [{:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm"},
"elixir_make": {:hex, :elixir_make, "0.5.0", "3d65ce6d537d2ce164c0311551a607b90ea7f91d85242a67f3c5e59693c3aa50", [:mix], [], "hexpm"},
"email_checker": {:hex, :email_checker, "0.1.2", "05b3121c71b69f1ab5df7d8b4844046898bf218031998ef53f20c6b8bfd219e9", [:mix], [{:socket, "~> 0.3.1", [hex: :socket, repo: "hexpm", optional: false]}], "hexpm"},
"erlex": {:hex, :erlex, "0.2.0", "80349ebd58553dbd63489937380bfa7d906be3266b91bbd9d2bd6b71f1e8c07d", [:mix], [], "hexpm"},
"ecto_sql": {:hex, :ecto_sql, "3.0.5", "7e44172b4f7aca4469f38d7f6a3da394dbf43a1bcf0ca975e958cb957becd74e", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.0.6", [hex: :ecto, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.9.1", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.3.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
"elixir_make": {:hex, :elixir_make, "0.5.2", "96a28c79f5b8d34879cd95ebc04d2a0d678cfbbd3e74c43cb63a76adf0ee8054", [:mix], [], "hexpm"},
"erlex": {:hex, :erlex, "0.2.1", "cee02918660807cbba9a7229cae9b42d1c6143b768c781fa6cee1eaf03ad860b", [:mix], [], "hexpm"},
"ex_crypto": {:hex, :ex_crypto, "0.9.0", "e04a831034c4d0a43fb2858f696d6b5ae0f87f07dedca3452912fd3cb5ee3ca2", [:mix], [{:poison, ">= 2.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.19.3", "3c7b0f02851f5fc13b040e8e925051452e41248f685e40250d7e40b07b9f8c10", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
"ex_ical": {:hex, :ex_ical, "0.2.0", "4b928b554614704016cc0c9ee226eb854da9327a1cc460457621ceacb1ac29a6", [:mix], [{:timex, "~> 3.1", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm"},
@ -42,11 +42,11 @@
"file_system": {:hex, :file_system, "0.2.6", "fd4dc3af89b9ab1dc8ccbcc214a0e60c41f34be251d9307920748a14bf41f1d3", [:mix], [], "hexpm"},
"gen_smtp": {:hex, :gen_smtp, "0.12.0", "97d44903f5ca18ca85cb39aee7d9c77e98d79804bbdef56078adcf905cb2ef00", [:rebar3], [], "hexpm"},
"geo": {:hex, :geo, "3.1.0", "727e005262430d037e870ff364e65d80ca5ca21d5ac8eddd57a1ada72c3f83b0", [:mix], [], "hexpm"},
"geo_postgis": {:hex, :geo_postgis, "2.1.0", "e0640d18276cb1dd58aeae3f5eed9a61641a5110901e1e35d0d662031d936b33", [:mix], [{:geo, "~> 3.0", [hex: :geo, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm"},
"geo_postgis": {:hex, :geo_postgis, "3.1.0", "d06c8fa5fd140a52a5c9dab4ad6623a696dd7d99dd791bb361d3f94942442ff9", [:mix], [{:geo, "~> 3.1", [hex: :geo, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0 or ~> 4.0", [hex: :poison, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm"},
"geolix": {:hex, :geolix, "0.17.0", "8f3f4068be08599912de67ae24372a6c148794a0152f9f83ffd5a2ffcb21d29a", [:mix], [{:mmdb2_decoder, "~> 0.3.0", [hex: :mmdb2_decoder, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.0", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm"},
"gettext": {:hex, :gettext, "0.16.1", "e2130b25eebcbe02bb343b119a07ae2c7e28bd4b146c4a154da2ffb2b3507af2", [:mix], [], "hexpm"},
"guardian": {:hex, :guardian, "1.2.1", "bdc8dd3dbf0fb7216cb6f91c11831faa1a64d39cdaed9a611e37f2413e584983", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"},
"guardian_db": {:hex, :guardian_db, "1.1.0", "45ab94206cce38f7443dc27de6dc52966ccbdeff65ca1b1f11a6d8f3daceb556", [:mix], [{:ecto, "~> 2.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:guardian, "~> 1.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm"},
"guardian_db": {:hex, :guardian_db, "2.0.0", "65019ee9e09e1cfc157c0ec7840747bc54959ed4b985da80fecae9ff19b0f5df", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:guardian, "~> 1.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm"},
"hackney": {:hex, :hackney, "1.15.0", "287a5d2304d516f63e56c469511c42b016423bcb167e61b611f6bad47e3ca60e", [:rebar3], [{:certifi, "2.4.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"http_sign": {:hex, :http_sign, "0.1.1", "b16edb83aa282892f3271f9a048c155e772bf36e15700ab93901484c55f8dd10", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"httpoison": {:hex, :httpoison, "1.5.0", "71ae9f304bdf7f00e9cd1823f275c955bdfc68282bc5eb5c85c3a9ade865d68e", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
@ -56,8 +56,6 @@
"jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
"json_ld": {:hex, :json_ld, "0.3.0", "92f508ca831b9e4530e3e6c950976fdafcf26323e6817c325b3e1ee78affc4bd", [:mix], [{:jason, "~> 1.1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:rdf, "~> 0.5", [hex: :rdf, repo: "hexpm", optional: false]}], "hexpm"},
"jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm"},
"kronky": {:hex, :kronky, "0.5.0", "b2038c267f02b297044cb574f542fa96763278a88b32a97d0c37bde95c63c13b", [:mix], [{:absinthe, "~> 1.3", [hex: :absinthe, repo: "hexpm", optional: false]}, {:ecto, ">= 2.1.4", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm"},
"littlefinger": {:hex, :littlefinger, "0.1.0", "5d3720bebd65d6a2051c31ca45f28b2d452d25aeeb8adb0a8f87013868bb0e7e", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
"makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
"meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm"},
@ -68,26 +66,24 @@
"mmdb2_decoder": {:hex, :mmdb2_decoder, "0.3.0", "03a159a52342d3328cf2b774f1036e56719f7edc7f919180588a7764854c3318", [:mix], [], "hexpm"},
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
"phoenix": {:hex, :phoenix, "1.4.0", "56fe9a809e0e735f3e3b9b31c1b749d4b436e466d8da627b8d82f90eaae714d2", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
"phoenix_ecto": {:hex, :phoenix_ecto, "3.6.0", "d65dbcedd6af568d8582dcd7da516c3051016bad51f9953e5337fea40bcd8a9d", [:mix], [{:ecto, "~> 2.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix": {:hex, :phoenix, "1.4.1", "801f9d632808657f1f7c657c8bbe624caaf2ba91429123ebe3801598aea4c3d9", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.0.0", "c43117a136e7399ea04ecaac73f8f23ee0ffe3e07acfcb8062fe5f4c9f0f6531", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_html": {:hex, :phoenix_html, "2.13.1", "fa8f034b5328e2dfa0e4131b5569379003f34bc1fafdaa84985b0b9d2f12e68b", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.0", "3bb31a9fbd40ffe8652e60c8660dffd72dd231efcdf49b744fb75b9ef7db5dd2", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.1", "6668d787e602981f24f17a5fbb69cc98f8ab085114ebfac6cc36e10a90c8e93c", [:mix], [], "hexpm"},
"plug": {:hex, :plug, "1.7.1", "8516d565fb84a6a8b2ca722e74e2cd25ca0fc9d64f364ec9dbec09d33eb78ccd", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}], "hexpm"},
"plug_cowboy": {:hex, :plug_cowboy, "1.0.0", "2e2a7d3409746d335f451218b8bb0858301c3de6d668c3052716c909936eb57a", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"plug": {:hex, :plug, "1.7.2", "d7b7db7fbd755e8283b6c0a50be71ec0a3d67d9213d74422d9372effc8e87fd1", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}], "hexpm"},
"plug_cowboy": {:hex, :plug_cowboy, "2.0.1", "d798f8ee5acc86b7d42dbe4450b8b0dadf665ce588236eb0a751a132417a980e", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm"},
"postgrex": {:hex, :postgrex, "0.13.5", "3d931aba29363e1443da167a4b12f06dcd171103c424de15e5f3fc2ba3e6d9c5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm"},
"ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"},
"postgrex": {:hex, :postgrex, "0.14.1", "63247d4a5ad6b9de57a0bac5d807e1c32d41e39c04b8a4156a26c63bcd8a2e49", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
"rdf": {:hex, :rdf, "0.5.4", "57e09d4adfe7646fe0c3514b703b76eaf29d537b250b36abae75e66d7e5920cf", [:mix], [{:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm"},
"rsa_ex": {:hex, :rsa_ex, "0.4.0", "e28dd7dc5236e156df434af0e4aa822384c8866c928e17b785d4edb7c253b558", [:mix], [], "hexpm"},
"slugger": {:hex, :slugger, "0.3.0", "efc667ab99eee19a48913ccf3d038b1fb9f165fa4fbf093be898b8099e61b6ed", [:mix], [], "hexpm"},
"socket": {:hex, :socket, "0.3.13", "98a2ab20ce17f95fb512c5cadddba32b57273e0d2dba2d2e5f976c5969d0c632", [:mix], [], "hexpm"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
"telemetry": {:hex, :telemetry, "0.3.0", "099a7f3ce31e4780f971b4630a3c22ec66d22208bc090fe33a2a3a6a67754a73", [:rebar3], [], "hexpm"},
"timex": {:hex, :timex, "3.5.0", "b0a23167da02d0fe4f1a4e104d1f929a00d348502b52432c05de875d0b9cffa5", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
"timex_ecto": {:hex, :timex_ecto, "3.3.0", "d5bdef09928e7a60f10a0baa47ce653f29b43d6fee87b30b236b216d0e36b98d", [:mix], [{:ecto, "~> 2.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:timex, "~> 3.1", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm"},
"tzdata": {:hex, :tzdata, "0.5.19", "7962a3997bf06303b7d1772988ede22260f3dae1bf897408ebdac2b4435f4e6a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
"uuid": {:hex, :uuid, "1.1.8", "e22fc04499de0de3ed1116b770c7737779f226ceefa0badb3592e64d5cfb4eb9", [:mix], [], "hexpm"},
}

@ -0,0 +1,22 @@
defmodule Mobilizon.Repo.Migrations.TagsRelations do
use Ecto.Migration
def up do
create table(:tag_relations, primary_key: false) do
add :tag_id, references(:tags, on_delete: :delete_all), null: false, primary_key: true
add :link_id, references(:tags, on_delete: :delete_all), null: false, primary_key: true
add :weight, :integer, null: false, default: 1
end
create constraint(:tag_relations, :no_self_loops_check, check: "tag_id <> link_id")
create index(:tag_relations, [:tag_id], name: :index_tag_relations_tag_id)
create index(:tag_relations, [:link_id], name: :index_tag_relations_link_id)
end
def down do
drop constraint(:tag_relations, :no_self_loops_check)
drop index(:tag_relations, [:tags_id])
drop index(:tag_relations, [:link_id])
drop table(:tag_relations)
end
end

@ -0,0 +1,48 @@
defmodule Mobilizon.Repo.Migrations.DropDatetimetz do
use Ecto.Migration
def up do
alter table(:events) do
remove(:begins_on)
remove(:ends_on)
remove(:publish_at)
add(:begins_on, :utc_datetime)
add(:ends_on, :utc_datetime)
add(:publish_at, :utc_datetime)
end
alter table(:sessions) do
remove(:begins_on)
remove(:ends_on)
add(:begins_on, :utc_datetime)
add(:ends_on, :utc_datetime)
end
execute "DROP TYPE datetimetz"
end
def down do
execute("""
CREATE TYPE datetimetz AS (
dt timestamptz,
tz varchar
);
""")
alter table(:events) do
remove(:begins_on)
remove(:ends_on)
remove(:publish_at)
add(:begins_on, :datetimetz)
add(:ends_on, :datetimetz)
add(:publish_at, :datetimetz)
end
alter table(:sessions) do
remove(:begins_on)
remove(:ends_on)
add(:begins_on, :datetimetz)
add(:ends_on, :datetimetz)
end
end
end

@ -21,17 +21,21 @@ actor = insert(:actor, user: user)
# Insert a second actor account for the same user
actor2 = insert(:actor, user: user)
tag1 = insert(:tag)
tag2 = insert(:tag)
tag3 = insert(:tag)
# Make actor organize a few events
event = insert(:event, organizer_actor: actor)
event2 = insert(:event, organizer_actor: actor)
event3 = insert(:event, organizer_actor: actor)
event4 = insert(:event, organizer_actor: actor2)
participant = insert(:participant, actor: actor, event: event, role: 4)
participant = insert(:participant, actor: actor, event: event2, role: 4)
participant = insert(:participant, actor: actor, event: event3, role: 4)
participant = insert(:participant, actor: actor2, event: event4, role: 4)
participant = insert(:participant, actor: actor, event: event4, role: 1)
event = insert(:event, organizer_actor: actor, tags: [tag1, tag2])
event2 = insert(:event, organizer_actor: actor, tags: [tag1, tag2])
event3 = insert(:event, organizer_actor: actor, tags: [tag1])
event4 = insert(:event, organizer_actor: actor2, tags: [tag3, tag2])
insert(:participant, actor: actor, event: event, role: :creator)
insert(:participant, actor: actor, event: event2, role: :creator)
insert(:participant, actor: actor, event: event3, role: :creator)
insert(:participant, actor: actor2, event: event4, role: :creator)
insert(:participant, actor: actor, event: event4, role: :participant)
# Insert a group
group = insert(:actor, type: :Group)

@ -2,12 +2,9 @@
"type": "Delete",
"signature": {
"type": "RsaSignature2017",
"signatureValue": "cw0RlfNREf+5VdsOYcCBDrv521eiLsDTAYNHKffjF0bozhCnOh+wHkFik7WamUk$
uEiN4L2H6vPlGRprAZGRhEwgy+A7rIFQNmLrpW5qV5UNVI/2F7kngEHqZQgbQYj9hW+5GMYmPkHdv3D72ZefGw$
4Xa2NBLGFpAjQllfzt7kzZLKKY2DM99FdUa64I2Wj3iD04Hs23SbrUdAeuGk/c1Cg6bwGNG4vxoiwn1jikgJLA$
NAlSGjsRGdR7LfbC7GqWWsW3cSNsLFPoU6FyALjgTrrYoHiXe0QHggw+L3yMLfzB2S/L46/VRbyb+WDKMBIXUL$
5owmzHSi6e/ZtCI3w==",
"creator": "http://mastodon.example.org/users/gargron#main-key", "created": "2018-03-03T16:24:11Z"
"signatureValue": "cw0RlfNREf+5VdsOYcCBDrv521eiLsDTAYNHKffjF0bozhCnOh+wHkFik7WamUkuEiN4L2H6vPlGRprAZGRhEwgy+A7rIFQNmLrpW5qV5UNVI/2F7kngEHqZQgbQYj9hW+5GMYmPkHdv3D72ZefGw4Xa2NBLGFpAjQllfzt7kzZLKKY2DM99FdUa64I2Wj3iD04Hs23SbrUdAeuGk/c1Cg6bwGNG4vxoiwn1jikgJLANAlSGjsRGdR7LfbC7GqWWsW3cSNsLFPoU6FyALjgTrrYoHiXe0QHggw+L3yMLfzB2S/L46/VRbyb+WDKMBIXUL5owmzHSi6e/ZtCI3w==",
"creator": "http://mastodon.example.org/users/gargron#main-key",
"created": "2018-03-03T16:24:11Z"
},
"object": {
"type": "Tombstone",

@ -373,7 +373,7 @@ defmodule Mobilizon.ActorsTest do
assert {:error, :user_not_found} = Actors.get_user_by_email(@email, true)
Actors.update_user(user, %{
"confirmed_at" => DateTime.utc_now(),
"confirmed_at" => DateTime.utc_now() |> DateTime.truncate(:second),
"confirmation_sent_at" => nil,
"confirmation_token" => nil
})

@ -6,9 +6,9 @@ defmodule Mobilizon.EventsTest do
alias Mobilizon.Events
@event_valid_attrs %{
begins_on: "2010-04-17 14:00:00.000000Z",
begins_on: "2010-04-17 14:00:00Z",
description: "some description",
ends_on: "2010-04-17 14:00:00.000000Z",
ends_on: "2010-04-17 14:00:00Z",
title: "some title",
url: "some url",
uuid: "b5126423-f1af-43e4-a923-002a03003ba4"
@ -24,15 +24,15 @@ defmodule Mobilizon.EventsTest do
end
@valid_attrs %{
begins_on: "2010-04-17 14:00:00.000000Z",
begins_on: "2010-04-17 14:00:00Z",
description: "some description",
ends_on: "2010-04-17 14:00:00.000000Z",
ends_on: "2010-04-17 14:00:00Z",
title: "some title"
}
@update_attrs %{
begins_on: "2011-05-18 15:01:01.000000Z",
begins_on: "2011-05-18 15:01:01Z",
description: "some updated description",
ends_on: "2011-05-18 15:01:01.000000Z",
ends_on: "2011-05-18 15:01:01Z",
title: "some updated title"
}
@invalid_attrs %{begins_on: nil, description: nil, ends_on: nil, title: nil}
@ -90,9 +90,9 @@ defmodule Mobilizon.EventsTest do
|> Map.put(:address_id, address.id)
with {:ok, %Event{} = event} <- Events.create_event(valid_attrs) do
assert event.begins_on == DateTime.from_naive!(~N[2010-04-17 14:00:00.000000Z], "Etc/UTC")
assert event.begins_on == DateTime.from_naive!(~N[2010-04-17 14:00:00Z], "Etc/UTC")
assert event.description == "some description"
assert event.ends_on == DateTime.from_naive!(~N[2010-04-17 14:00:00.000000Z], "Etc/UTC")
assert event.ends_on == DateTime.from_naive!(~N[2010-04-17 14:00:00Z], "Etc/UTC")
assert event.title == "some title"
else
err ->
@ -107,9 +107,9 @@ defmodule Mobilizon.EventsTest do
test "update_event/2 with valid data updates the event", %{event: event} do
assert {:ok, event} = Events.update_event(event, @update_attrs)
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:01Z], "Etc/UTC")
assert event.description == "some updated description"
assert event.ends_on == DateTime.from_naive!(~N[2011-05-18 15:01:01.000000Z], "Etc/UTC")
assert event.ends_on == DateTime.from_naive!(~N[2011-05-18 15:01:01Z], "Etc/UTC")
assert event.title == "some updated title"
end
@ -302,6 +302,81 @@ defmodule Mobilizon.EventsTest do
end
end
describe "tags_relations" do
alias Mobilizon.Events.TagRelation
alias Mobilizon.Events.Tag
setup do
tag1 = insert(:tag)
tag2 = insert(:tag)
{:ok, tag1: tag1, tag2: tag2}
end
test "create_tag_relation/1 with valid data creates a tag relation", %{
tag1: %Tag{id: tag1_id} = tag1,
tag2: %Tag{id: tag2_id} = tag2
} do
assert {:ok, %TagRelation{} = tag_relation} =
Events.create_tag_relation(%{tag_id: tag1_id, link_id: tag2_id})
assert Events.are_tags_linked(tag1, tag2)
assert Events.are_tags_linked(tag2, tag1)
end
test "create_tag_relation/1 with invalid data returns error changeset", %{
tag1: %Tag{} = tag1,
tag2: %Tag{} = tag2
} do
assert {:error, %Ecto.Changeset{}} =
Events.create_tag_relation(%{tag_id: nil, link_id: nil})
refute Events.are_tags_linked(tag1, tag2)
end
test "delete_tag_relation/1 deletes the tag relation" do
tag_relation = insert(:tag_relation)
assert {:ok, %TagRelation{}} = Events.delete_tag_relation(tag_relation)
end
test "tag_neighbors/2 return the connected tags for a given tag", %{
tag1: %Tag{} = tag1,
tag2: %Tag{} = tag2
} do
tag3 = insert(:tag)
tag4 = insert(:tag)
assert {:ok, %TagRelation{}} =
Events.create_tag_relation(%{tag_id: tag1.id, link_id: tag2.id})
assert {:ok, %TagRelation{}} =
Events.create_tag_relation(%{tag_id: tag2.id, link_id: tag1.id})
assert {:ok, %TagRelation{}} =
Events.create_tag_relation(%{tag_id: tag3.id, link_id: tag2.id})
assert {:ok, %TagRelation{}} =
Events.create_tag_relation(%{tag_id: tag4.id, link_id: tag1.id})
assert {:ok, %TagRelation{}} =
Events.create_tag_relation(%{tag_id: tag4.id, link_id: tag1.id})
assert {:ok, %TagRelation{}} =
Events.create_tag_relation(%{tag_id: tag4.id, link_id: tag1.id})
assert {:error,
%Ecto.Changeset{
errors: [
tag_id:
{"Can't add a relation on self",
[constraint: :check, constraint_name: "no_self_loops_check"]}
]
}} = Events.create_tag_relation(%{tag_id: tag1.id, link_id: tag1.id})
# The order is preserved, since tag4 has one more relation than tag2
assert [tag4, tag2] == Events.tag_neighbors(tag1)
end
end
describe "participants" do
alias Mobilizon.Events.{Participant, Event}
alias Mobilizon.Actors.Actor

@ -34,7 +34,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# data =
# File.read!("test/fixtures/mastodon-post-activity.json")
# |> Poison.decode!()
# |> Jason.decode!()
# |> Map.put("object", activity["object"])
# {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
@ -45,7 +45,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# test "it fetches replied-to activities if we don't have them" do
# data =
# File.read!("test/fixtures/mastodon-post-activity.json")
# |> Poison.decode!()
# |> Jason.decode!()
# object =
# data["object"]
@ -69,7 +69,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# end
test "it works for incoming notices" do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
{:ok, %Mobilizon.Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
@ -103,7 +103,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
end
test "it works for incoming notices with hashtags" do
data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Poison.decode!()
data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Jason.decode!()
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
assert Enum.at(data["object"]["tag"], 2) == "moo"
@ -111,7 +111,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# test "it works for incoming notices with contentMap" do
# data =
# File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Poison.decode!()
# File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Jason.decode!()
# {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
@ -120,7 +120,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# end
# test "it works for incoming notices with to/cc not being an array (kroeg)" do
# data = File.read!("test/fixtures/kroeg-post-activity.json") |> Poison.decode!()
# data = File.read!("test/fixtures/kroeg-post-activity.json") |> Jason.decode!()
# {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
@ -129,7 +129,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# end
# test "it works for incoming announces with actor being inlined (kroeg)" do
# data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!()
# data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Jason.decode!()
# {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
@ -137,7 +137,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# end
# test "it works for incoming notices with tag not being an array (kroeg)" do
# data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!()
# data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Jason.decode!()
# {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
@ -145,7 +145,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# "icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png"
# }
# data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!()
# data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Jason.decode!()
# {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
@ -153,7 +153,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# end
test "it works for incoming notices with url not being a string (prismo)" do
data = File.read!("test/fixtures/prismo-url-map.json") |> Poison.decode!()
data = File.read!("test/fixtures/prismo-url-map.json") |> Jason.decode!()
assert {:error, :not_supported} == Transmogrifier.handle_incoming(data)
# Pages are not supported
@ -167,7 +167,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
data =
File.read!("test/fixtures/mastodon-follow-activity.json")
|> Poison.decode!()
|> Jason.decode!()
|> Map.put("object", actor.url)
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
@ -185,7 +185,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# data =
# File.read!("test/fixtures/hubzilla-follow-activity.json")
# |> Poison.decode!()
# |> Jason.decode!()
# |> Map.put("object", user.ap_id)
# |> Utils.normalize_params()
@ -202,7 +202,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# data =
# File.read!("test/fixtures/mastodon-like.json")
# |> Poison.decode!()
# |> Jason.decode!()
# |> Map.put("object", url)
# {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
@ -218,7 +218,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# data =
# File.read!("test/fixtures/mastodon-undo-like.json")
# |> Poison.decode!()
# |> Jason.decode!()
# |> Map.put("object", url)
# assert Transmogrifier.handle_incoming(data) == {:error, :not_supported}
@ -229,14 +229,14 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# like_data =
# File.read!("test/fixtures/mastodon-like.json")
# |> Poison.decode!()
# |> Jason.decode!()
# |> Map.put("object", comment.url)
# {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data)
# data =
# File.read!("test/fixtures/mastodon-undo-like.json")
# |> Poison.decode!()
# |> Jason.decode!()
# |> Map.put("object", like_data)
# |> Map.put("actor", like_data["actor"])
@ -249,7 +249,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# end
# test "it works for incoming announces" do
# data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!()
# data = File.read!("test/fixtures/mastodon-announce.json") |> Jason.decode!()
# {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
@ -270,7 +270,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# data =
# File.read!("test/fixtures/mastodon-announce.json")
# |> Poison.decode!()
# |> Jason.decode!()
# |> Map.put("object", comment.url)
# {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
@ -287,10 +287,10 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# end
test "it works for incoming update activities" do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
update_data = File.read!("test/fixtures/mastodon-update.json") |> Jason.decode!()
object =
update_data["object"]
@ -317,10 +317,10 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
end
# test "it works for incoming update activities which lock the account" do
# data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
# data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
# {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
# update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
# update_data = File.read!("test/fixtures/mastodon-update.json") |> Jason.decode!()
# object =
# update_data["object"]
@ -345,7 +345,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
data =
File.read!("test/fixtures/mastodon-delete.json")
|> Poison.decode!()
|> Jason.decode!()
object =
data["object"]
@ -369,7 +369,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# data =
# File.read!("test/fixtures/mastodon-delete.json")
# |> Poison.decode!()
# |> Jason.decode!()
# object =
# data["object"]
@ -389,7 +389,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# announce_data =
# File.read!("test/fixtures/mastodon-announce.json")
# |> Poison.decode!()
# |> Jason.decode!()
# |> Map.put("object", comment.url)
# {:ok, %Activity{data: announce_data, local: false}} =
@ -397,7 +397,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
# data =
# File.read!("test/fixtures/mastodon-undo-announce.json")
# |> Poison.decode!()
# |> Jason.decode!()
# |> Map.put("object", announce_data)
# |> Map.put("actor", announce_data["actor"])
@ -416,14 +416,14 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
follow_data =
File.read!("test/fixtures/mastodon-follow-activity.json")
|> Poison.decode!()
|> Jason.decode!()
|> Map.put("object", actor.url)
{:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data)
data =
File.read!("test/fixtures/mastodon-unfollow-activity.json")
|> Poison.decode!()
|> Jason.decode!()
|> Map.put("object", follow_data)
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
@ -442,7 +442,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do