Commit 46bf8097 authored by Mayel's avatar Mayel

Merge branch 'extension/activitypub' of ssh://gitlab.com/CommonsPub/Server...

Merge branch 'extension/activitypub' of ssh://gitlab.com/CommonsPub/Server into extension/activitypub
parents 770342af 3cdd5ad9
Pipeline #214855166 failed with stage
in 1 minute and 15 seconds
......@@ -19,7 +19,7 @@ defmodule CommonsPub.Collections.Collection do
table_schema "mn_collection" do
# belongs_to(:actor, Character)
has_one(:character, CommonsPub.Characters.Character, references: :id, foreign_key: :id)
has_one(:character, Character, references: :id, foreign_key: :id)
belongs_to(:creator, User)
......
......@@ -24,8 +24,6 @@ defmodule CommonsPub.Communities do
alias CommonsPub.Users.User
alias CommonsPub.Workers.APPublishWorker
alias CommonsPub.Utils.Web.CommonHelper
### Cursor generators
def cursor(:followers), do: &[&1.follower_count, &1.id]
......
......@@ -112,7 +112,7 @@ type GeolocationQuery {
limit: Int
before: [Cursor]
after: [Cursor]
): [SpatialThingsPage!]
): SpatialThingsPage!
}
type GeolocationMutation {
......
......@@ -36,8 +36,8 @@ defmodule Measurement.Hydration do
unit: [
resolve: &Measurement.Unit.GraphQL.unit/2
],
measures: [
resolve: &Measurement.Measure.GraphQL.measures/2
measures_pages: [
resolve: &Measurement.Measure.GraphQL.measures_pages/2
],
# all_measures: [
# resolve: &Measurement.Measure.GraphQL.all_measures/2
......
......@@ -29,7 +29,7 @@ defmodule Measurement.Measure.GraphQL do
})
end
def measures(page_opts, info) do
def measures_pages(page_opts, info) do
ResolveRootPage.run(%ResolveRootPage{
module: __MODULE__,
fetcher: :fetch_measures,
......@@ -53,10 +53,10 @@ defmodule Measurement.Measure.GraphQL do
FetchPage.run(%FetchPage{
queries: Measurement.Measure.Queries,
query: Measurement.Measure,
# cursor_fn: measures.cursor(:followers),
cursor_fn: & &1.id,
page_opts: page_opts,
base_filters: [user: GraphQL.current_user(info)],
data_filters: [:dafault]
data_filters: [:default]
})
end
......
......@@ -85,6 +85,7 @@ defmodule Measurement.Measure.Queries do
## by field values
def filter(q, {:id, id}) when is_binary(id) do
where(q, [measure: c], c.id == ^id)
end
......@@ -120,30 +121,6 @@ defmodule Measurement.Measure.Queries do
select(q, [measure: c], {field(c, ^key), count(c.id)})
end
# pagination
def filter(q, {:limit, limit}) do
limit(q, ^limit)
end
def filter(q, {:paginate_id, %{after: a, limit: limit}}) do
limit = limit + 2
q
|> where([measure: c], c.id >= ^a)
|> limit(^limit)
end
def filter(q, {:paginate_id, %{before: b, limit: limit}}) do
q
|> where([measure: c], c.id <= ^b)
|> filter(limit: limit + 2)
end
def filter(q, {:paginate_id, %{limit: limit}}) do
filter(q, limit: limit + 1)
end
# defp page(q, %{limit: limit}, _), do: filter(q, limit: limit + 1)
def inc_quantity(id, amount) do
......
......@@ -140,11 +140,11 @@ type MeasuresPage {
type MeasurementQuery {
unit(id: ID): Unit
units(start: ID, limit: Int): [Unit!]
unitsPages(limit: Int, before: [Cursor], after: [Cursor]): [UnitsPage!]
unitsPages(limit: Int, before: [Cursor], after: [Cursor]): UnitsPage!
measure(id: ID): Measure
measures(start: ID, limit: Int): [Measure!]
measuresPages(limit: Int, before: [Cursor], after: [Cursor]): [MeasuresPage!]
measuresPages(limit: Int, before: [Cursor], after: [Cursor]): MeasuresPage!
}
type MeasurementMutation {
......
......@@ -3,7 +3,6 @@ defmodule CommonsPub.Tag.Simulate do
@moduledoc false
import CommonsPub.Utils.Simulation
import CommonsPub.Utils.Trendy
alias CommonsPub.Tag.Categories
......
# SPDX-License-Identifier: AGPL-3.0-only
defmodule ValueFlows.Claim do
use Pointers.Pointable,
otp_app: :commons_pub,
source: "vf_claim",
table_id: "C0MM0NSPVBVA1VEF10WSC1A1MS"
import CommonsPub.Common.Changeset, only: [change_public: 1, change_disabled: 1]
alias Ecto.Changeset
alias CommonsPub.Users.User
alias Measurement.Measure
alias ValueFlows.Knowledge.Action
alias ValueFlows.Knowledge.ResourceSpecification
alias ValueFlows.Observation.EconomicEvent
@type t :: %__MODULE__{}
pointable_schema do
field(:note, :string)
field(:agreed_in, :string)
field(:finished, :boolean)
field(:created, :utc_datetime_usec)
field(:due, :utc_datetime_usec)
field(:resource_classified_as, {:array, :string})
belongs_to(:action, Action, type: :string)
belongs_to(:provider, Pointers.Pointer)
belongs_to(:receiver, Pointers.Pointer)
belongs_to(:resource_quantity, Measure, on_replace: :nilify)
belongs_to(:effort_quantity, Measure, on_replace: :nilify)
belongs_to(:resource_conforms_to, ResourceSpecification)
belongs_to(:triggered_by, EconomicEvent)
# a.k.a. in_scope_of
belongs_to(:context, Pointers.Pointer)
# not defined in spec, used internally
belongs_to(:creator, User)
field(:is_public, :boolean, virtual: true)
field(:published_at, :utc_datetime_usec)
field(:is_disabled, :boolean, virtual: true, default: false)
field(:disabled_at, :utc_datetime_usec)
field(:deleted_at, :utc_datetime_usec)
timestamps(inserted_at: false)
end
@required ~w(action_id)a
@cast @required ++
~w(note finished agreed_in created due resource_classified_as is_disabled)a ++
~w(context_id resource_conforms_to_id triggered_by_id)a
def create_changeset(%User{} = creator, %{id: _} = provider, %{id: _} = receiver, attrs) do
%__MODULE__{}
|> Changeset.cast(attrs, @cast)
|> Changeset.change(
creator_id: creator.id,
provider_id: provider.id,
receiver_id: receiver.id,
is_public: true
)
|> common_changeset(attrs)
end
def update_changeset(%__MODULE__{} = claim, attrs) do
claim
|> Changeset.cast(attrs, @cast)
|> common_changeset(attrs)
end
def validate_required(changeset) do
Changeset.validate_required(changeset, @required)
end
defp common_changeset(changeset, attrs) do
changeset
|> change_measures(attrs)
|> change_public()
|> change_disabled()
end
def change_measures(changeset, %{} = attrs) do
measures = Map.take(attrs, measure_fields())
Enum.reduce(measures, changeset, fn {field_name, measure}, c ->
Changeset.put_assoc(c, field_name, measure)
end)
end
def measure_fields, do: [:resource_quantity, :effort_quantity]
end
# SPDX-License-Identifier: AGPL-3.0-only
defmodule ValueFlows.Claim.Claims do
import CommonsPub.Common, only: [maybe_put: 3]
alias CommonsPub.Repo
alias CommonsPub.Users.User
alias ValueFlows.Claim
alias ValueFlows.Claim.Queries
alias CommonsPub.Meta.Pointers
def one(filters), do: Repo.single(Queries.query(Claim, filters))
def many(filters \\ []), do: {:ok, Repo.all(Queries.query(Claim, filters))}
def preload_all(%Claim{} = claim) do
# shouldn't fail
{:ok, claim} = one(id: claim.id, preload: :all)
claim
end
def create(%User{} = creator, %{id: _} = provider, %{id: _} = receiver, %{} = attrs) do
Repo.transact_with(fn ->
attrs = prepare_attrs(attrs)
with {:ok, provider_ptr} <- Pointers.one(id: provider.id),
{:ok, receiver_ptr} <- Pointers.one(id: receiver.id) do
Claim.create_changeset(creator, provider_ptr, receiver_ptr, attrs)
|> Claim.validate_required()
|> Repo.insert()
|> CommonsPub.Common.maybe_ok_error(&preload_all/1)
end
end)
end
def update(%Claim{} = claim, %{} = attrs) do
Repo.transact_with(fn ->
attrs = prepare_attrs(attrs)
claim
|> Claim.update_changeset(attrs)
|> Repo.update()
|> CommonsPub.Common.maybe_ok_error(&preload_all/1)
end)
end
def soft_delete(%Claim{} = claim) do
CommonsPub.Common.Deletion.soft_delete(claim)
end
defp prepare_attrs(attrs) do
attrs
|> maybe_put(:action_id, Map.get(attrs, :action))
|> maybe_put(:context_id,
attrs |> Map.get(:in_scope_of) |> CommonsPub.Common.maybe(&List.first/1)
)
|> maybe_put(:resource_conforms_to_id, Map.get(attrs, :resource_conforms_to))
|> maybe_put(:triggered_by_id, Map.get(attrs, :triggered_by))
end
end
# SPDX-License-Identifier: AGPL-3.0-only
defmodule ValueFlows.Claim.GraphQL do
# alias CommonsPub.Web.GraphQL.{CommonResolver}
require Logger
# use Absinthe.Schema.Notation
# import_sdl path: "lib/value_flows/graphql/schemas/claim.gql"
alias CommonsPub.{GraphQL, Repo}
alias CommonsPub.Meta.Pointers
alias CommonsPub.GraphQL.{FetchPage, ResolveField, ResolveRootPage}
alias ValueFlows.Claim.Claims
def claim(%{id: id}, info) do
ResolveField.run(%ResolveField{
module: __MODULE__,
fetcher: :fetch_claim,
context: id,
info: info
})
end
def claims(page_opts, info) do
ResolveRootPage.run(%ResolveRootPage{
module: __MODULE__,
fetcher: :fetch_claims,
page_opts: page_opts,
info: info,
cursor_validators: [&(is_integer(&1) and &1 >= 0), &Ecto.ULID.cast/1]
})
end
def fetch_claim(_info, id) do
Claims.one([:default, id: id])
end
def fetch_events(page_opts, info) do
FetchPage.run(%FetchPage{
queries: ValueFlows.Claim.Queries,
query: ValueFlows.Claim,
page_opts: page_opts,
base_filters: [
:default,
user: GraphQL.current_user(info)
]
})
end
def fetch_resource_conforms_to_edge(%{resource_conforms_to_id: id} = thing, _, _)
when is_binary(id) do
thing = Repo.preload(thing, :resource_conforms_to)
{:ok, Map.get(thing, :resource_conforms_to)}
end
def fetch_resource_conforms_to_edge(_, _, _) do
{:ok, nil}
end
def fetch_triggered_by_edge(%{triggered_by_id: id} = thing, _, _) when is_binary(id) do
thing = Repo.preload(thing, :triggered_by)
{:ok, Map.get(thing, :triggered_by)}
end
def fetch_triggered_by_edge(_, _, _) do
{:ok, nil}
end
def create_claim(%{claim: %{provider: provider_id, receiver: receiver_id} = attrs}, info) do
with {:ok, user} <- GraphQL.current_user_or_not_logged_in(info),
{:ok, provider} <- Pointers.one(id: provider_id),
{:ok, receiver} <- Pointers.one(id: receiver_id),
{:ok, claim} <- Claims.create(user, provider, receiver, attrs) do
{:ok, %{claim: claim}}
end
end
def update_claim(%{claim: %{id: id} = attrs}, info) do
with {:ok, _user} <- GraphQL.current_user_or_not_logged_in(info),
{:ok, claim} <- claim(%{id: id}, info),
{:ok, claim} <- Claims.update(claim, attrs) do
{:ok, %{claim: claim}}
end
end
def delete_claim(%{id: id}, info) do
with {:ok, _user} <- GraphQL.current_user_or_not_logged_in(info),
{:ok, claim} <- claim(%{id: id}, info),
{:ok, _} <- Claims.soft_delete(claim) do
{:ok, true}
end
end
end
# SPDX-License-Identifier: AGPL-3.0-only
defmodule ValueFlows.Claim.Migrations do
use Ecto.Migration
import Pointers.Migration
alias ValueFlows.Knowledge.ResourceSpecification
alias ValueFlows.Observation.EconomicEvent
def up do
create_pointable_table(ValueFlows.Claim) do
add(:note, :text)
add(:agreed_in, :text)
add(:action_id, :string)
add(:finished, :boolean)
add(:created, :timestamptz)
add(:due, :timestamptz)
add(:resource_classified_as, {:array, :string})
add(:provider_id, weak_pointer(), null: true)
add(:receiver_id, weak_pointer(), null: true)
add(:resource_conforms_to_id, weak_pointer(ResourceSpecification), null: true)
add(:triggered_by_id, weak_pointer(EconomicEvent), null: true)
add(:resource_quantity_id, weak_pointer(Measurement.Measure), null: true)
add(:effort_quantity_id, weak_pointer(Measurement.Measure), null: true)
add(:creator_id, references("mn_user", on_delete: :nilify_all))
add(:context_id, weak_pointer(), null: true)
add(:published_at, :timestamptz)
add(:deleted_at, :timestamptz)
add(:disabled_at, :timestamptz)
timestamps(inserted_at: false, type: :utc_datetime_usec)
end
end
def down do
drop_pointable_table(ValueFlows.Claim)
end
end
# SPDX-License-Identifier: AGPL-3.0-only
defmodule ValueFlows.Claim.Queries do
import CommonsPub.Common.Query, only: [match_admin: 0]
import Ecto.Query
alias CommonsPub.Follows.Follow
alias CommonsPub.Users.User
alias ValueFlows.Claim
def query(Claim) do
from(c in Claim, as: :claim)
end
def query(q, filters), do: filter(query(q), filters)
def queries(query, _page_opts, base_filters, data_filters, count_filters) do
base_q = query(query, base_filters)
data_q = filter(base_q, data_filters)
count_q = filter(base_q, count_filters)
{data_q, count_q}
end
def join_to(q, spec, join_qualifier \\ :left)
def join_to(q, specs, jq) when is_list(specs) do
Enum.reduce(specs, q, &join_to(&2, &1, jq))
end
def join_to(q, :context, jq) do
join(q, jq, [claim: c], c2 in assoc(c, :context), as: :context)
end
def join_to(q, {:follow, follower_id}, jq) do
join(q, jq, [claim: c], f in Follow,
as: :follow,
on: c.id == f.context_id and f.creator_id == ^follower_id
)
end
def filter(q, filters) when is_list(filters) do
Enum.reduce(filters, q, &filter(&2, &1))
end
def filter(q, {:join, {join, qual}}), do: join_to(q, join, qual)
def filter(q, {:join, join}), do: join_to(q, join)
## by status
def filter(q, :default) do
filter(q, [:deleted])
end
def filter(q, :deleted) do
where(q, [claim: c], is_nil(c.deleted_at))
end
def filter(q, :disabled) do
where(q, [claim: c], is_nil(c.disabled_at))
end
def filter(q, :private) do
where(q, [claim: c], not is_nil(c.published_at))
end
## by user
def filter(q, {:creator, match_admin()}), do: q
def filter(q, {:creator, nil}) do
filter(q, ~w(disabled private)a)
end
def filter(q, {:creator, %User{id: id}}) do
q
|> join_to(follow: id)
|> where([claim: c, follow: f], not is_nil(c.published_at) or not is_nil(f.id))
|> filter(~w(disabled)a)
end
## by field values
def filter(q, {:id, id}) when is_binary(id) do
where(q, [claim: c], c.id == ^id)
end
def filter(q, {:id, ids}) when is_list(ids) do
where(q, [claim: c], c.id in ^ids)
end
def filter(q, {:provider_id, id}) when is_binary(id) do
where(q, [claim: c], c.provider_id == ^id)
end
def filter(q, {:provider_id, ids}) when is_list(ids) do
where(q, [claim: c], c.provider_id in ^ids)
end
def filter(q, {:receiver_id, id}) when is_binary(id) do
where(q, [claim: c], c.receiver_id == ^id)
end
def filter(q, {:receiver_id, ids}) when is_list(ids) do
where(q, [claim: c], c.receiver_id in ^ids)
end
def filter(q, {:context_id, id}) when is_binary(id) do
where(q, [claim: c], c.context_id == ^id)
end
def filter(q, {:context_id, ids}) when is_list(ids) do
where(q, [claim: c], c.context_id in ^ids)
end
def filter(q, {:action_id, ids}) when is_list(ids) do
where(q, [claim: c], c.action_id in ^ids)
end
def filter(q, {:action_id, id}) when is_binary(id) do
where(q, [claim: c], c.action_id == ^id)
end
## preloading
def filter(q, {:preload, :all}) do
preload(q, [
:creator,
:provider,
:receiver,
:resource_conforms_to,
:resource_quantity,
:effort_quantity,
:context,
:triggered_by,