commit b17125d85520a2e4f043777be6439c6e0ca0878b
parent 7d48e0bb16461a3bce5416b6e99b0130fa115182
Author: sir fish <dev@srf.sh>
Date: Tue, 8 Nov 2022 11:13:01 +0000
Merge pull request #32 from dyne/refactor
Zenflows{,Test}: refactor
Diffstat:
153 files changed, 4600 insertions(+), 3540 deletions(-)
diff --git a/src/zenflows/db/filter.ex b/src/zenflows/db/filter.ex
@@ -1,49 +0,0 @@
-# Zenflows is designed to implement the Valueflows vocabulary,
-# written and maintained by srfsh <info@dyne.org>.
-# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-defmodule Zenflows.DB.Filter do
-@moduledoc "Filtering helpers for Filter modules."
-
-alias Ecto.Changeset
-
-@type params() :: %{atom() => term()}
-@type error() :: {:error, Changeset.t()}
-@type result() :: {:ok, Ecto.Query.t()} | error()
-
-def escape_like(v) do
- Regex.replace(~r/\\|%|_/, v, &"\\#{&1}")
-end
-
-@doc """
-Changeset helper to check that `a` and `b` are not provided at the same time.
-"""
-@spec check_xor(Changeset.t(), atom(), atom()) :: Changeset.t()
-def check_xor(cset, a, b) do
- x = Changeset.get_change(cset, a)
- y = Changeset.get_change(cset, b)
-
- if x && y do
- msg = "can't provide both"
-
- cset
- |> Changeset.add_error(a, msg)
- |> Changeset.add_error(b, msg)
- else
- cset
- end
-end
-end
diff --git a/src/zenflows/db/page.ex b/src/zenflows/db/page.ex
@@ -0,0 +1,93 @@
+# Zenflows is designed to implement the Valueflows vocabulary,
+# written and maintained by srfsh <info@dyne.org>.
+# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+defmodule Zenflows.DB.Page do
+@moduledoc "Paging utilities to page results."
+
+import Ecto.Query
+
+alias Zenflows.DB.{ID, Repo}
+
+@enforce_keys ~w[dir cur num filter]a
+defstruct [:cur, :num, :filter, dir: :forw]
+
+@typedoc """
+Represents a generic struct that has all required information to
+(possibly filter and) paginate the rows of the database.
+
+The fields are:
+* `dir` - represents the *direction* that the database should query.
+* `cur` - represents the *cursor* handle to use to paginate.
+* `num` - represents the *number* of rows (+1) to fetch.
+* `filter` - represents the filters to be used for querying.
+"""
+
+@type t() :: %__MODULE__{
+ dir: :forw | :back,
+ cur: nil | ID.t(),
+ num: non_neg_integer(),
+ filter: nil | map(),
+}
+
+@doc """
+Parses a half-baked map/keyword into a `t:t()`.
+
+You should use this function for functions that expect `t:t()`, and
+you don't have any other means to generate a `t:t()` (such as with
+`Zenflows.GQL.Connection.parse/2` that converts Relay-specific
+paging information into `t:t()`).
+"""
+@spec new(map() | Keyword.t()) :: t()
+def new(_ \\ %{})
+def new(params) when is_list(params), do: Map.new(params) |> new()
+def new(params) when is_map(params) do
+ %__MODULE__{
+ dir: params[:dir] || :forw,
+ cur: params[:cur],
+ num: params[:num] || Zenflows.GQL.Connection.def_page_size(),
+ filter: params[:filter],
+ }
+end
+
+@doc """
+Similar to `c:Ecto.Repo.all/2`, but the result is paginated.
+
+Basically, transforms the queryable `q` into another query that
+limits the amount of returned records according to `dir`, `cur` and
+`num` (read `t:t()` on what those variables are used for).
+"""
+@spec all(Ecto.Queryable.t(), t()) :: [Ecto.Schema.t()]
+def all(q, %{dir: dir, cur: cur, num: num}) do
+ order_by =
+ case dir do
+ :forw -> [asc: :id]
+ :back -> [desc: :id]
+ end
+ where =
+ case {dir, cur} do
+ {_, nil} -> []
+ {:forw, cur} -> dynamic([x], x.id > ^cur)
+ {:back, cur} -> dynamic([x], x.id < ^cur)
+ end
+ from(x in q,
+ where: ^where,
+ order_by: ^order_by,
+ limit: ^num + 1,
+ select: x)
+ |> Repo.all()
+end
+end
diff --git a/src/zenflows/db/paging.ex b/src/zenflows/db/paging.ex
@@ -1,166 +0,0 @@
-# Zenflows is designed to implement the Valueflows vocabulary,
-# written and maintained by srfsh <info@dyne.org>.
-# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-defmodule Zenflows.DB.Paging do
-@moduledoc "Paging helpers for Domain modules."
-
-import Ecto.Query
-
-alias Zenflows.DB.{ID, Repo}
-
-@type result() :: {:ok, t()} | {:error, String.t()}
-
-@type t() :: %{
- page_info: page_info(),
- edges: [edges()],
-}
-
-@type page_info() :: %{
- start_cursor: ID.t() | nil,
- end_cursor: ID.t() | nil,
- has_previous_page: boolean(),
- has_next_page: boolean(),
- total_count: non_neg_integer(),
- page_limit: non_neg_integer(),
-}
-
-@type edges() :: %{
- cursor: ID.t(),
- node: struct(),
-}
-
-@type params() :: %{atom() => term()}
-
-@spec def_page_size() :: non_neg_integer()
-def def_page_size() do
- conf()[:def_page_size]
-end
-
-@spec max_page_size() :: non_neg_integer()
-def max_page_size() do
- conf()[:max_page_size]
-end
-
-@spec conf() :: Keyword.t()
-defp conf() do
- Application.fetch_env!(:zenflows, Zenflows.GQL)
-end
-
-@spec parse(params()) :: {:error, String.t()} | {:ok, {:forw | :back, ID.t() | nil, non_neg_integer()}}
-def parse(%{first: _, last: _}), do: {:error, "first and last can't be provided at the same time"}
-def parse(%{after: _, before: _}), do: {:error, "after and before can't be provided at the same time"}
-
-def parse(%{after: _, last: _}), do: {:error, "after and last can't be provided at the same time"}
-def parse(%{before: _, first: _}), do: {:error, "before and first can't be provided at the same time"}
-
-def parse(%{first: num}) when num < 0, do: {:error, "first must be positive"}
-def parse(%{last: num}) when num < 0, do: {:error, "last must be positive"}
-
-def parse(%{after: cur, first: num}), do: {:ok, {:forw, cur, normalize(num)}}
-def parse(%{after: cur}), do: {:ok, {:forw, cur, def_page_size()}}
-
-def parse(%{before: cur, last: num}), do: {:ok, {:back, cur, normalize(num)}}
-def parse(%{before: cur}), do: {:ok, {:back, cur, def_page_size()}}
-
-def parse(%{first: num}), do: {:ok, {:forw, nil, normalize(num)}}
-def parse(%{last: num}), do: {:ok, {:back, nil, normalize(num)}}
-
-def parse(_), do: {:ok, {:forw, nil, def_page_size()}}
-
-@spec normalize(integer()) :: non_neg_integer()
-defp normalize(num) do
- # credo:disable-for-next-line Credo.Check.Refactor.MatchInCondition
- if num > (max = max_page_size()),
- do: max,
- else: num
-end
-
-@doc """
-Page Ecto schemas.
-
-Only supports forward or backward paging with or without cursors.
-"""
-@spec page(atom() | Ecto.Query.t(), params()) :: result()
-def page(schema_or_query, params) do
- with {:ok, {dir, cur, num}} <- parse(params) do
- {page_fun, order_by} =
- case dir do
- :forw -> {&forw/3, [asc: :id]}
- :back -> {&back/3, [desc: :id]}
- end
- where =
- case {dir, cur} do
- {_, nil} -> []
- {:forw, cur} -> dynamic([s], s.id > ^cur)
- {:back, cur} -> dynamic([s], s.id < ^cur)
- end
- {:ok,
- from(s in schema_or_query,
- where: ^where,
- order_by: ^order_by,
- limit: ^num + 1,
- select: %{cursor: s.id, node: s})
- |> Repo.all()
- |> page_fun.(cur, num)}
- end
-end
-
-@spec forw(edges(), ID.t() | nil, non_neg_integer()) :: t()
-def forw(edges, cur, num) do
- {edges, count} =
- Enum.reduce(edges, {[], 0}, fn e, {edges, count} ->
- {[e | edges], count + 1}
- end)
-
- {edges, has_next?, count} =
- # we indeed have fetched num+1 records
- if count - 1 == num do
- [_ | edges] = edges
- {edges, true, count - 1}
- else
- {edges, false, count}
- end
-
- {edges, first, last} =
- case edges do
- [] -> {[], nil, nil}
- _ ->
- [last | _] = edges
- [first | _] = edges = Enum.reverse(edges)
- {edges, first, last}
- end
-
- %{
- edges: edges,
- page_info: %{
- start_cursor: first[:cursor],
- end_cursor: last[:cursor],
- has_next_page: has_next?,
- has_previous_page: cur != nil,
- total_count: count,
- page_limit: num,
- },
- }
-end
-
-@spec back(edges(), ID.t() | nil, non_neg_integer()) :: t()
-def back(edges, cur, num) do
- # Currently, this part of the algorithm doesn't care about
- # whether we do forward or backward paging.
- forw(edges, cur, num)
-end
-end
diff --git a/src/zenflows/db/schema.ex b/src/zenflows/db/schema.ex
@@ -20,21 +20,14 @@ defmodule Zenflows.DB.Schema do
Just a wrapper around Ecto.Schema to customize it.
"""
-@type params() :: %{required(binary()) => term()} | %{required(atom()) => term()}
+@type t() :: Ecto.Schema.t()
@type id() :: Zenflows.DB.ID.t()
+@type params() :: %{required(binary()) => term()} | %{required(atom()) => term()}
-defmacro __using__(opts) do
- types? = Keyword.get(opts, :types?, true)
-
+defmacro __using__(_) do
quote do
use Ecto.Schema
- alias Ecto.{Changeset, Schema}
-
- if unquote(types?) do
- @typep params() :: Zenflows.DB.Schema.params()
- end
-
@primary_key {:id, Zenflows.DB.ID, autogenerate: true}
@foreign_key_type Zenflows.DB.ID
@timestamps_opts type: :utc_datetime_usec, inserted_at: false
diff --git a/src/zenflows/db/validate.ex b/src/zenflows/db/validate.ex
@@ -0,0 +1,371 @@
+# Zenflows is designed to implement the Valueflows vocabulary,
+# written and maintained by srfsh <info@dyne.org>.
+# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+defmodule Zenflows.DB.Validate do
+@moduledoc "Ecto.Changeset validation helpers."
+
+alias Ecto.Changeset
+
+require Logger
+
+@typedoc """
+Used to specify whether to fetch the field's value from the `:changes`,
+`:data`, or `:both` of an `Ecto.Changeset`, respectively.
+
+Basically, uses `Ecto.Changeset.fetch_field/2` behind the scenes,
+but `nil` values will mean empty/not-provided.
+"""
+@type fetch_method() :: :change | :data | :both
+
+@doc """
+Escape possible characters that could represent expressions in SQL's
+`LIKE` keyword of a field. The value is changed if it is present.
+"""
+@spec escape_like(Changeset.t(), atom()) :: Changeset.t()
+def escape_like(cset, field) do
+ case Changeset.fetch_change(cset, field) do
+ {:ok, v} ->
+ v = Regex.replace(~r/\\|%|_/, v, &"\\#{&1}")
+ Changeset.put_change(cset, field, v)
+ :error -> cset
+ end
+end
+
+@doc """
+Use the OR logic on the existance of `fields`, and error if the
+result is false.
+
+Note: `fields` must contain at least 2 items, and make sure to read
+`t:fetch_method()`'s doc.
+
+Here's a truth table as a quick reference:
+
+| p | q | = |
+|---|---|---|
+| 0 | 0 | 0 |
+| 1 | 0 | 1 |
+| 0 | 1 | 1 |
+| 1 | 1 | 1 |
+"""
+@spec exist_or(Changeset.t(), [atom(), ...], Keyword.t()) :: Changeset.t()
+def exist_or(cset, fields, opts \\ []) do
+ meth = Keyword.get(opts, :method, :change)
+ if Enum.any?(fields, &field_exists?(meth, cset, &1)) do
+ cset
+ else
+ Enum.reduce(fields, cset,
+ &Changeset.add_error(&2, &1, "at least one of them must be provided"))
+ end
+end
+
+@doc """
+Use the XOR logic on the existance of `fields`, and error if the
+result is false.
+
+Note: `fields` must contain at least 2 items, and make sure to read
+`t:fetch_method()`'s doc.
+
+Here's a truth table as a quick reference:
+
+| p | q | = |
+|---|---|---|
+| 0 | 0 | 0 |
+| 1 | 0 | 1 |
+| 0 | 1 | 1 |
+| 1 | 1 | 0 |
+"""
+@spec exist_xor(Changeset.t(), [atom(), ...], Keyword.t()) :: Changeset.t()
+def exist_xor(cset, [h | t] = fields, opts \\ []) do
+ meth = Keyword.get(opts, :method, :change)
+ h_exists? = field_exists?(meth, cset, h)
+
+ if Enum.all?(t, &(h_exists? != field_exists?(meth, cset, &1))) do
+ cset
+ else
+ Enum.reduce(fields, cset,
+ &Changeset.add_error(&2, &1, "exactly one of them must be provided"))
+ end
+end
+
+@doc """
+Use the NAND logic on the existance of `fields`, and error if the
+result is false.
+
+Note: `fields` must contain at least 2 items, and make sure to
+read `t:fetch_method()`'s doc.
+
+Here's a truth table as a quick reference:
+
+| p | q | = |
+|---|---|---|
+| 0 | 0 | 1 |
+| 1 | 0 | 1 |
+| 0 | 1 | 1 |
+| 1 | 1 | 0 |
+"""
+@spec exist_nand(Changeset.t(), [atom(), ...], Keyword.t()) :: Changeset.t()
+def exist_nand(cset, fields, opts \\ []) do
+ meth = Keyword.get(opts, :method, :change)
+ if Enum.all?(fields, &field_exists?(meth, cset, &1)) do
+ Enum.reduce(fields, cset,
+ &Changeset.add_error(&2, &1, "one or none of them must be provided"))
+ else
+ cset
+ end
+end
+
+@doc """
+Compare the values of `fields`, and error if they aren't equal to
+each other.
+
+Note: `fields` must contain at least 2 items, and make sure to read
+`t:fetch_method()`'s doc. Also, empty fields will be skipped to
+allow more flexibility. Use `Ecto.Changeset.validate_required/3`
+to achieve otherwise.
+
+Here's a truth table as a quick reference:
+
+| p | q | = |
+|---|---|---|
+| 0 | 0 | 1 |
+| 1 | 0 | 0 |
+| 0 | 1 | 0 |
+| 1 | 1 | 1 |
+"""
+@spec value_eq(Changeset.t(), [atom(), ...], Keyword.t()) :: Changeset.t()
+def value_eq(cset, [a, b], opts \\ []) do
+ meth = Keyword.get(opts, :method, :change)
+ with {:ok, x} <- field_fetch(meth, cset, a),
+ {:ok, y} <- field_fetch(meth, cset, b) do
+ if x == y do
+ cset
+ else
+ msg = "all of them must be the same"
+ cset
+ |> Changeset.add_error(a, msg)
+ |> Changeset.add_error(b, msg)
+ end
+ else _ ->
+ cset
+ end
+end
+
+@doc """
+Compare the values of `fields`, and error if they aren't all
+different.
+
+Note: `fields` must contain at least 2 items, and make sure to read
+`t:fetch_method()`'s doc. Also, empty fields will be skipped to
+allow more flexibility. Use `Ecto.Changeset.validate_required/3`
+to achieve otherwise.
+
+Here's a truth table as a quick reference:
+
+| p | q | = |
+|---|---|---|
+| 0 | 0 | 1 |
+| 1 | 0 | 1 |
+| 0 | 1 | 1 |
+| 1 | 1 | 0 |
+"""
+@spec value_ne(Changeset.t(), [atom(), ...], Keyword.t()) :: Changeset.t()
+def value_ne(cset, [a, b], opts \\ []) do
+ meth = Keyword.get(opts, :method, :change)
+ with {:ok, x} <- field_fetch(meth, cset, a),
+ {:ok, y} <- field_fetch(meth, cset, b) do
+ if x != y do
+ cset
+ else
+ msg = "all of them must be different"
+ cset
+ |> Changeset.add_error(a, msg)
+ |> Changeset.add_error(b, msg)
+ end
+ else _ ->
+ cset
+ end
+end
+
+@doc "Validate that given `field` is a valid email address."
+@spec email(Changeset.t(), atom()) :: Changeset.t()
+def email(cset, field) do
+ # works good enough for now
+ Changeset.validate_format(cset, field, ~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])?)*$/)
+end
+
+@doc """
+Validate that given `field` is [1, 256] bytes long.
+
+The name "name" is a reference to short texts, as in email subject,
+usernames, full names, and so on.
+"""
+@spec name(Changeset.t(), atom()) :: Changeset.t()
+def name(cset, field), do: byte_range(cset, field, 1, 256)
+
+@doc """
+Validate that given `field` is [1, 2048] bytes long.
+
+The name "note" is a reference to long texts, as in email bodies,
+descriptions, notes, and so on.
+"""
+@spec note(Changeset.t(), atom()) :: Changeset.t()
+def note(cset, field), do: byte_range(cset, field, 1, 2048)
+
+@doc """
+Validate that given `field` is [16, 2048] bytes long.
+
+The name "key" is a reference to encoded cryptographic keys (so the
+size is not the size of the binary key).
+"""
+@spec key(Changeset.t(), atom()) :: Changeset.t()
+def key(cset, field), do: byte_range(cset, field, 16, 2048)
+
+@doc """
+Validate that given `field` is [1, 512] bytes long.
+
+The name "uri" is a reference to not-literal URIs, but a size used
+for mostly URIs.
+"""
+@spec uri(Changeset.t(), atom()) :: Changeset.t()
+def uri(cset, field), do: byte_range(cset, field, 1, 512)
+
+@doc """
+Validate that given `field` is [1B, 25MiB] long, and log a warning
+if it is longer than 4MiB.
+
+The name "img" is a reference to Base64-encoded image binary data.
+"""
+@spec img(Changeset.t(), atom()) :: Changeset.t()
+def img(cset, field) do
+ Changeset.validate_change(cset, field, __MODULE__, fn
+ _, str when byte_size(str) < 1 ->
+ [{field, "should be at least 1B long"}]
+ _, str when byte_size(str) > 25 * 1024 * 1024 ->
+ [{field, "should be at most 25MiB long"}]
+ _, str when byte_size(str) > 4 * 1024 * 1024 ->
+ Logger.warning("file exceeds 4MiB")
+ []
+ _, _ ->
+ []
+ end)
+end
+
+@doc """
+Validate that given `field` is a list of binaries, which are [1,
+512] bytes long in size, and the list itself contains [1, 128]
+items.
+
+The name "class" is a reference to list of strings used for tagging,
+categorization and so on.
+"""
+@spec class(Changeset.t(), atom()) :: Changeset.t()
+def class(cset, field) do
+ Changeset.validate_change(cset, field, __MODULE__, fn
+ _, [] ->
+ [{field, "must contain at least 1 item"}]
+ _, list ->
+ case do_class(list, 0, 128) do
+ {:exceeds, _ind} ->
+ [{field, "must contain at most 128 items"}]
+ {:short, ind} ->
+ [{field, "the item at #{ind + 1} cannot be shorter than 1 byte"}]
+ {:long, ind} ->
+ [{field, "the item at #{ind + 1} cannot be longer than 512 bytes"}]
+ {:valid, _ind} ->
+ []
+ end
+ end)
+end
+
+# The rationale of this function is to loop over the list while
+# decreasing `rem` (reminder) and increasing `ind` (index) until
+# either one of these happen (in that order):
+#
+# 1. `rem` is equal to 0
+# 2. one of the items in the list is shorter than 1 byte long
+# 3. one of the items in the list is longer than 512 bytes long
+#
+@spec do_class([String.t()], non_neg_integer(), non_neg_integer())
+ :: {:exceeds | :short | :long | :valid, non_neg_integer()}
+defp do_class([], ind, _), do: {:valid, ind - 1}
+defp do_class([h | t], ind, rem) do
+ cond do
+ rem == 0 -> {:exceeds, ind}
+ byte_size(h) < 1 -> {:short, ind}
+ byte_size(h) > 512 -> {:long, ind}
+ true -> do_class(t, ind + 1, rem - 1)
+ end
+end
+
+@doc """
+Validate that the given binary (in Elixir terms) is in this inclusive
+octects/bytes range.
+"""
+@spec byte_range(Changeset.t(), atom(), non_neg_integer(), non_neg_integer())
+ :: Changeset.t()
+def byte_range(cset, field, min, max) do
+ Changeset.validate_change(cset, field, __MODULE__, fn
+ _, bin when byte_size(bin) < min ->
+ [{field, "should be at least #{min} byte(s) long"}]
+ _, bin when byte_size(bin) > max ->
+ [{field, "should be at most #{max} byte(s) long"}]
+ _, _ ->
+ []
+ end)
+end
+
+@spec field_exists?(fetch_method(), Changeset.t(), atom()) :: boolean()
+defp field_exists?(:change, cset, field) do
+ case Changeset.fetch_field(cset, field) do
+ {:changes, x} when not is_nil(x) -> true
+ _ -> false
+ end
+end
+defp field_exists?(:data, cset, field) do
+ case Changeset.fetch_field(cset, field) do
+ {:data, x} when not is_nil(x) -> true
+ _ -> false
+ end
+end
+defp field_exists?(:both, cset, field) do
+ case Changeset.fetch_field(cset, field) do
+ {:changes, x} when not is_nil(x) -> true
+ {:data, x} when not is_nil(x) -> true
+ _ -> false
+ end
+end
+
+@spec field_fetch(fetch_method(), Changeset.t(), atom()) :: {:ok, term()} | :error
+defp field_fetch(:change, cset, field) do
+ case Changeset.fetch_field(cset, field) do
+ {:changes, v} -> {:ok, v}
+ _ -> :error
+ end
+end
+defp field_fetch(:data, cset, field) do
+ case Changeset.fetch_field(cset, field) do
+ {:data, v} -> {:ok, v}
+ _ -> :error
+ end
+end
+defp field_fetch(:both, cset, field) do
+ case Changeset.fetch_field(cset, field) do
+ :error -> :error
+ {_, v} -> {:ok, v}
+ end
+end
+end
diff --git a/src/zenflows/file.ex b/src/zenflows/file.ex
@@ -24,13 +24,14 @@ use Zenflows.DB.Schema
require Logger
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.VF.{
Agent,
EconomicResource,
Intent,
RecipeResource,
ResourceSpecification,
- Validate,
}
@type t() :: %__MODULE__{
@@ -78,8 +79,8 @@ end
resource_specification_id intent_id
]a
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/file/domain.ex b/src/zenflows/file/domain.ex
@@ -18,13 +18,11 @@
defmodule Zenflows.File.Domain do
@moduledoc "Domain logic of Files."
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.Changeset
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.File
-@typep repo() :: Ecto.Repo.t()
-@typep id() :: Zenflows.DB.Schema.id()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, File.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -35,8 +33,20 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(File, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: File.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [File.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(File, page)}
+end
+
+@spec all!(Page.t()) :: [File.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
end
end
diff --git a/src/zenflows/gql/connection.ex b/src/zenflows/gql/connection.ex
@@ -0,0 +1,143 @@
+# Zenflows is designed to implement the Valueflows vocabulary,
+# written and maintained by srfsh <info@dyne.org>.
+# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+defmodule Zenflows.GQL.Connection do
+@moduledoc "GraphQL Relay Connection helpers."
+
+alias Ecto.Changeset
+alias Zenflows.DB.{ID, Page, Schema, Validate}
+
+@enforce_keys [:page_info, :edges]
+defstruct @enforce_keys
+
+@type t() :: %__MODULE__{
+ page_info: page_info(),
+ edges: [edge()],
+}
+
+@type page_info() :: %{
+ start_cursor: ID.t() | nil,
+ end_cursor: ID.t() | nil,
+ has_previous_page: boolean(),
+ has_next_page: boolean(),
+ total_count: non_neg_integer(),
+ page_limit: non_neg_integer(),
+}
+
+@type edge() :: %{
+ cursor: ID.t(),
+ node: Ecto.Schema.t(),
+}
+
+@doc """
+Converts the given list of schemas (that you get by using
+`Zenflows.DB.Page.all()`) into a Relay connection.
+"""
+@spec from_list([Ecto.Schema.t()], Page.t()) :: t()
+def from_list(records, %{cur: cur, num: num}) do
+ # Currently, we don't differenciate between forwards or
+ # backwards paging, so we only care whether `cur` is nil or
+ # not.
+
+ {edges, count} =
+ Enum.reduce(records, {[], 0}, fn r, {edges, count} ->
+ {[%{cursor: r.id, node: r} | edges], count + 1}
+ end)
+
+ {edges, has_next?, count} =
+ # we indeed have fetched num+1 records
+ if count - 1 == num do
+ [_ | edges] = edges
+ {edges, true, count - 1}
+ else
+ {edges, false, count}
+ end
+
+ {edges, first, last} =
+ case edges do
+ [] -> {[], nil, nil}
+ _ ->
+ [last | _] = edges
+ [first | _] = edges = Enum.reverse(edges)
+ {edges, first, last}
+ end
+
+ %__MODULE__{
+ edges: edges,
+ page_info: %{
+ start_cursor: first[:cursor],
+ end_cursor: last[:cursor],
+ has_next_page: has_next?,
+ has_previous_page: cur != nil,
+ total_count: count,
+ page_limit: num,
+ },
+ }
+end
+
+@doc """
+Parses a Relay-specific map with filters into a generic
+`t:Zenflows.DB.Page.t()`.
+"""
+@spec parse(Schema.params()) :: {:ok, Page.t()} | {:error, Changeset.t()}
+def parse(params) do
+ with {:ok, data} <- Changeset.apply_action(changeset(params), nil) do
+ after_ = data[:after]
+ first = data[:first]
+ before = data[:before]
+ last = data[:last]
+
+ {:ok, %Page{
+ dir: if(before || last, do: :back, else: :forw),
+ cur: after_ || before,
+ num: if((n = first || last), do: normalize(n), else: def_page_size()),
+ filter: data[:filter],
+ }}
+ end
+end
+
+@doc false
+@spec changeset(Schema.params()) :: Changeset.t()
+def changeset(params) do
+ {%{}, %{after: ID, first: :integer, before: ID, last: :integer, filter: :map}}
+ |> Changeset.cast(params, [:after, :first, :before, :last, :filter])
+ |> Changeset.validate_number(:first, greater_than_or_equal_to: 0)
+ |> Changeset.validate_number(:last, greater_than_or_equal_to: 0)
+ |> Validate.exist_nand([:first, :last])
+ |> Validate.exist_nand([:after, :before])
+ |> Validate.exist_nand([:after, :last])
+ |> Validate.exist_nand([:before, :first])
+end
+
+@spec normalize(non_neg_integer()) :: non_neg_integer()
+defp normalize(num) do
+ # credo:disable-for-next-line Credo.Check.Refactor.MatchInCondition
+ if num > (max = max_page_size()),
+ do: max, else: num
+end
+
+@doc "The default page size for paging."
+@spec def_page_size() :: non_neg_integer()
+def def_page_size(), do: Keyword.fetch!(conf(), :def_page_size)
+
+@doc "The maximum page size for paging."
+@spec max_page_size() :: non_neg_integer()
+def max_page_size(), do: Keyword.fetch!(conf(), :max_page_size)
+
+@spec conf() :: Keyword.t()
+defp conf(), do: Application.fetch_env!(:zenflows, Zenflows.GQL)
+end
diff --git a/src/zenflows/keypairoom/domain.ex b/src/zenflows/keypairoom/domain.ex
@@ -24,14 +24,15 @@ tasks.
alias Zenflows.Restroom
alias Zenflows.VF.Person
-def keypairoom_server(false, data) do
- if Person.Domain.exists?(email: Map.fetch!(data, "email")),
+@spec keypairoom_server(boolean(), map()) :: {:ok, String.t()} | {:error, term()}
+def keypairoom_server(false, %{"email" => email} = data) do
+ if Person.Domain.exists?(email: email),
do: Restroom.keypairoom_server(data),
else: {:error, "email doesn't exists"}
end
-def keypairoom_server(true, data) do
- if Person.Domain.exists?(email: Map.fetch!(data, "email")),
+def keypairoom_server(true, %{"email" => email} = data) do
+ if Person.Domain.exists?(email: email),
do: {:error, "email exists"},
else: Restroom.keypairoom_server(data)
end
diff --git a/src/zenflows/sw_pass/domain.ex b/src/zenflows/sw_pass/domain.ex
@@ -66,7 +66,7 @@ def import_repos(url) do
case repo.get_by(ResourceSpecification, params) do
nil ->
params
- |> ResourceSpecification.chgset()
+ |> ResourceSpecification.changeset()
|> repo.insert()
res_spec -> {:ok, res_spec}
end
@@ -77,7 +77,7 @@ def import_repos(url) do
case repo.get_by(ResourceSpecification, params) do
nil ->
params
- |> ResourceSpecification.chgset()
+ |> ResourceSpecification.changeset()
|> repo.insert()
res_spec -> {:ok, res_spec}
end
@@ -88,7 +88,7 @@ def import_repos(url) do
case repo.get_by(Unit, params) do
nil ->
params
- |> Unit.chgset()
+ |> Unit.changeset()
|> repo.insert()
unit -> {:ok, unit}
end
@@ -99,7 +99,7 @@ def import_repos(url) do
case repo.get_by(Process, params) do
nil ->
params
- |> Process.chgset()
+ |> Process.changeset()
|> repo.insert()
proc -> {:ok, proc}
end
@@ -112,7 +112,7 @@ def import_repos(url) do
case repo.get_by(Person, params) do
nil ->
params
- |> Person.chgset()
+ |> Person.changeset()
|> repo.insert()
per -> {:ok, per}
end
@@ -132,7 +132,7 @@ def import_repos(url) do
nil ->
params
|> Map.put(:has_point_in_time, now)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
|> repo.insert()
evt -> {:ok, evt}
end
@@ -165,7 +165,7 @@ def import_repos(url) do
has_numerical_value: 1,
})
|> Map.put(:has_point_in_time, now)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
|> repo.insert()
evt -> {:ok, evt}
end
@@ -185,7 +185,7 @@ def import_repos(url) do
case repo.get_by(EconomicResource, params) do
nil ->
params
- |> EconomicResource.chgset()
+ |> EconomicResource.changeset()
|> repo.insert()
res -> {:ok, res}
end
diff --git a/src/zenflows/validate.ex b/src/zenflows/validate.ex
@@ -0,0 +1,326 @@
+# Zenflows is designed to implement the Valueflows vocabulary,
+# written and maintained by srfsh <info@dyne.org>.
+# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+defmodule Zenflows.Validate do
+@moduledoc "Ecto.Changeset validation helpers."
+
+alias Ecto.Changeset
+
+require Logger
+
+@typedoc """
+Used to specify whether to fetch the field's value from the `:changes`,
+`:data`, or `:both` of an `Ecto.Changeset`, respectively.
+
+Basically, uses `Ecto.Changeset.fetch_field/2` behind the scenes,
+but `nil` values will mean empty/not-provided.
+"""
+@type fetch_method() :: :change | :data | :both
+
+@doc """
+Escape possible characters that could represent expressions in SQL's
+`LIKE` keyword of a field. The value is changed if it is present.
+"""
+@spec escape_like(Changeset.t(), atom()) :: Changeset.t()
+def escape_like(cset, field) do
+ case Changeset.fetch_change(cset, :field) do
+ {:ok, v} ->
+ v = Regex.replace(~r/\\|%|_/, v, &"\\#{&1}")
+ Changeset.put_change(cset, field, v)
+ :error -> cset
+ end
+end
+
+@doc """
+Use the OR logic on the existance of `fields`, and error if the
+result is false.
+
+Note: `fields` must contain at least 2 items, and make sure to read
+`t:fetch_method()`'s doc.
+"""
+@spec exist_or(Changeset.t(), [atom(), ...], Keyword.t()) :: Changeset.t()
+def exist_or(cset, fields, opts \\ []) do
+ meth = Keyword.get(opts, :method, :change)
+ if Enum.any?(fields, &field_exists?(meth, cset, &1)) do
+ cset
+ else
+ Enum.reduce(fields, cset,
+ &Changeset.add_error(&2, &1, "at least one of them must be provided"))
+ end
+end
+
+@doc """
+Use the XOR logic on the existance of `fields`, and error if the
+result is false.
+
+Note: `fields` must contain at least 2 items, and make sure to read
+`t:fetch_method()`'s doc.
+"""
+@spec exist_xor(Changeset.t(), [atom(), ...], Keyword.t()) :: Changeset.t()
+def exist_xor(cset, [h | t] = fields, opts \\ []) do
+ meth = Keyword.get(opts, :method, :change)
+ h_exists? = field_exists?(meth, cset, h)
+
+ if Enum.all?(t, &(h_exists? != field_exists?(meth, cset, &1))) do
+ cset
+ else
+ Enum.reduce(fields, cset,
+ &Changeset.add_error(&2, &1, "exactly one of them must be provided"))
+ end
+end
+
+@doc """
+Use the NAND logic on the existance of `fields`, and error if the
+result is false.
+
+Note: `fields` must contain at least 2 items, and make sure to
+read `t:fetch_method()`'s doc.
+"""
+@spec exist_nand(Changeset.t(), [atom(), ...], Keyword.t()) :: Changeset.t()
+def exist_nand(cset, fields, opts \\ []) do
+ meth = Keyword.get(opts, :method, :change)
+ if Enum.all?(fields, &field_exists?(meth, cset, &1)) do
+ Enum.reduce(fields, cset,
+ &Changeset.add_error(&2, &1, "one or none of them must be provided"))
+ else
+ cset
+ end
+end
+
+@doc """
+Compare the values of `fields`, and error if they aren't equal to
+each other.
+
+Note: `fields` must contain at least 2 items, and make sure to read
+`t:fetch_method()`'s doc. Also, empty fields will be skipped to
+allow more flexibility. Use `Ecto.Changeset.validate_required/3`
+to achieve otherwise.
+"""
+@spec value_eq(Changeset.t(), [atom(), ...], Keyword.t()) :: Changeset.t()
+def value_eq(cset, [a, b], opts \\ []) do
+ meth = Keyword.get(opts, :method, :change)
+ with {:ok, x} <- field_fetch(meth, cset, a),
+ {:ok, y} <- field_fetch(meth, cset, b) do
+ if x == y do
+ cset
+ else
+ msg = "all of them must be the same"
+ cset
+ |> Changeset.add_error(a, msg)
+ |> Changeset.add_error(b, msg)
+ end
+ else _ ->
+ cset
+ end
+end
+
+@doc """
+Compare the values of `fields`, and error if they aren't all
+different.
+
+Note: `fields` must contain at least 2 items, and make sure to read
+`t:fetch_method()`'s doc. Also, empty fields will be skipped to
+allow more flexibility. Use `Ecto.Changeset.validate_required/3`
+to achieve otherwise.
+"""
+@spec value_ne(Changeset.t(), [atom(), ...], Keyword.t()) :: Changeset.t()
+def value_ne(cset, [a, b], opts \\ []) do
+ meth = Keyword.get(opts, :method, :change)
+ with {:ok, x} <- field_fetch(meth, cset, a),
+ {:ok, y} <- field_fetch(meth, cset, b) do
+ if x != y do
+ cset
+ else
+ msg = "all of them must be different"
+ cset
+ |> Changeset.add_error(a, msg)
+ |> Changeset.add_error(b, msg)
+ end
+ else _ ->
+ cset
+ end
+end
+
+@doc "Validate that given `field` is a valid email address."
+@spec email(Changeset.t(), atom()) :: Changeset.t()
+def email(cset, field) do
+ # works good enough for now
+ Changeset.validate_format(cset, field, ~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])?)*$/)
+end
+
+@doc """
+Validate that given `field` is [1, 256] bytes long.
+
+The name "name" is a reference to short texts, as in email subject,
+usernames, full names, and so on.
+"""
+@spec name(Changeset.t(), atom()) :: Changeset.t()
+def name(cset, field), do: byte_range(cset, field, 1, 256)
+
+@doc """
+Validate that given `field` is [1, 2048] bytes long.
+
+The name "note" is a reference to long texts, as in email bodies,
+descriptions, notes, and so on.
+"""
+@spec note(Changeset.t(), atom()) :: Changeset.t()
+def note(cset, field), do: byte_range(cset, field, 1, 2048)
+
+@doc """
+Validate that given `field` is [16, 2048] bytes long.
+
+The name "key" is a reference to encoded cryptographic keys (so the
+size is not the size of the binary key).
+"""
+@spec key(Changeset.t(), atom()) :: Changeset.t()
+def key(cset, field), do: byte_range(cset, field, 16, 2048)
+
+@doc """
+Validate that given `field` is [1, 512] bytes long.
+
+The name "uri" is a reference to not-literal URIs, but a size used
+for mostly URIs.
+"""
+@spec uri(Changeset.t(), atom()) :: Changeset.t()
+def uri(cset, field), do: byte_range(cset, field, 1, 512)
+
+@doc """
+Validate that given `field` is [1B, 25MiB] long, and log a warning
+if it is longer than 4MiB.
+
+The name "img" is a reference to Base64-encoded image binary data.
+"""
+@spec img(Changeset.t(), atom()) :: Changeset.t()
+def img(cset, field) do
+ Changeset.validate_change(cset, field, __MODULE__, fn
+ _, str when byte_size(str) < 1 ->
+ [{field, "should be at least 1B long"}]
+ _, str when byte_size(str) > 25 * 1024 * 1024 ->
+ [{field, "should be at most 25MiB long"}]
+ _, str when byte_size(str) > 4 * 1024 * 1024 ->
+ Logger.warning("file exceeds 4MiB")
+ []
+ _, _ ->
+ []
+ end)
+end
+
+@doc """
+Validate that given `field` is a list of binaries, which are [1,
+512] bytes long in size, and the list itself contains [1, 128]
+items.
+
+The name "class" is a reference to list of strings used for tagging,
+categorization and so on.
+"""
+@spec class(Changeset.t(), atom()) :: Changeset.t()
+def class(cset, field) do
+ Changeset.validate_change(cset, field, __MODULE__, fn
+ _, [] ->
+ [{field, "must contain at least 1 item"}]
+ _, list ->
+ case do_class(list, 0, 128) do
+ {:exceeds, _ind} ->
+ [{field, "must contain at most 128 items"}]
+ {:short, ind} ->
+ [{field, "the item at #{ind + 1} cannot be shorter than 1 byte"}]
+ {:long, ind} ->
+ [{field, "the item at #{ind + 1} cannot be longer than 512 bytes"}]
+ {:valid, _ind} ->
+ []
+ end
+ end)
+end
+
+# The rationale of this function is to loop over the list while
+# decreasing `rem` (reminder) and increasing `ind` (index) until
+# either one of these happen (in that order):
+#
+# 1. `rem` is equal to 0
+# 2. one of the items in the list is shorter than 1 byte long
+# 3. one of the items in the list is longer than 512 bytes long
+#
+@spec do_class([String.t()], non_neg_integer(), non_neg_integer())
+ :: {:exceeds | :short | :long | :valid, non_neg_integer()}
+defp do_class([], ind, _), do: {:valid, ind - 1}
+defp do_class([h | t], ind, rem) do
+ cond do
+ rem == 0 -> {:exceeds, ind}
+ byte_size(h) < 1 -> {:short, ind}
+ byte_size(h) > 512 -> {:long, ind}
+ true -> do_class(t, ind + 1, rem - 1)
+ end
+end
+
+@doc """
+Validate that the given binary (in Elixir terms) is in this inclusive
+octects/bytes range.
+"""
+@spec byte_range(Changeset.t(), atom(), non_neg_integer(), non_neg_integer())
+ :: Changeset.t()
+def byte_range(cset, field, min, max) do
+ Changeset.validate_change(cset, field, __MODULE__, fn
+ _, bin when byte_size(bin) < min ->
+ [{field, "should be at least #{min} byte(s) long"}]
+ _, bin when byte_size(bin) > max ->
+ [{field, "should be at most #{max} byte(s) long"}]
+ _, _ ->
+ []
+ end)
+end
+
+@spec field_exists?(fetch_method(), Changeset.t(), atom()) :: boolean()
+defp field_exists?(:change, cset, field) do
+ case Changeset.fetch_field(cset, field) do
+ {:changes, x} when not is_nil(x) -> true
+ _ -> false
+ end
+end
+defp field_exists?(:data, cset, field) do
+ case Changeset.fetch_field(cset, field) do
+ {:data, x} when not is_nil(x) -> true
+ _ -> false
+ end
+end
+defp field_exists?(:both, cset, field) do
+ case Changeset.fetch_field(cset, field) do
+ {:changes, x} when not is_nil(x) -> true
+ {:data, x} when not is_nil(x) -> true
+ _ -> false
+ end
+end
+
+@spec field_fetch(fetch_method(), Changeset.t(), atom()) :: {:ok, term()} | :error
+defp field_fetch(:change, cset, field) do
+ case Changeset.fetch_field(cset, field) do
+ {:changes, v} -> {:ok, v}
+ _ -> :error
+ end
+end
+defp field_fetch(:data, cset, field) do
+ case Changeset.fetch_field(cset, field) do
+ {:data, v} -> {:ok, v}
+ _ -> :error
+ end
+end
+defp field_fetch(:both, cset, field) do
+ case Changeset.fetch_field(cset, field) do
+ :error -> :error
+ {_, v} -> {:ok, v}
+ end
+end
+end
diff --git a/src/zenflows/vf/action/type.ex b/src/zenflows/vf/action/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Action.Type do
-@moduledoc "GraphQL types of Actions."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/agent.ex b/src/zenflows/vf/agent.ex
@@ -20,7 +20,7 @@ defmodule Zenflows.VF.Agent do
A person or group or organization with economic agency.
"""
-use Zenflows.DB.Schema, types?: false
+use Zenflows.DB.Schema
alias Zenflows.File
alias Zenflows.VF.SpatialThing
diff --git a/src/zenflows/vf/agent/domain.ex b/src/zenflows/vf/agent/domain.ex
@@ -18,13 +18,12 @@
defmodule Zenflows.VF.Agent.Domain do
@moduledoc "Domain logic of Agents."
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.Changeset
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.{Agent, Agent.Filter}
-@typep repo() :: Ecto.Repo.t()
-@typep id() :: Zenflows.DB.Schema.id()
-
-@spec one(repo(), id() | map() | Keyword.t()) :: {:ok, Agent.t()} | {:error, String.t()}
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
+ :: {:ok, Agent.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
def one(repo, clauses) do
@@ -34,19 +33,27 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Filter.error() | Paging.result()
-def all(params \\ %{}) do
- with {:ok, q} <- Filter.filter(params[:filter] || %{}) do
- Paging.page(q, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: Agent.t()
+def one!(repo \\ Repo, x) do
+ {:ok, found} = one(repo, x)
+ found
+end
+
+@spec all(Page.t()) :: {:ok, [Agent.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ with {:ok, q} <- Filter.all(page) do
+ {:ok, Page.all(q, page)}
end
end
-@spec preload(Agent.t(), :images | :primary_location) :: Agent.t()
-def preload(agent, :images) do
- Repo.preload(agent, :images)
+@spec all!(Page.t()) :: [Agent.t()]
+def all!(page \\ Page.new()) do
+ {:ok, q} = Filter.all(page)
+ Page.all(q, page)
end
-def preload(agent, :primary_location) do
- Repo.preload(agent, :primary_location)
+@spec preload(Agent.t(), :images | :primary_location) :: Agent.t()
+def preload(agent, x) when x in ~w[images primary_location]a do
+ Repo.preload(agent, x)
end
end
diff --git a/src/zenflows/vf/agent/filter.ex b/src/zenflows/vf/agent/filter.ex
@@ -16,40 +16,33 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Agent.Filter do
-@moduledoc "Filtering logic of Agents."
-
-use Zenflows.DB.Schema
+@moduledoc false
import Ecto.Query
-alias Ecto.Query
-alias Zenflows.DB.Filter
-alias Zenflows.VF.{Agent, Validate}
-
-@type error() :: Filter.error()
+alias Ecto.{Changeset, Queryable}
+alias Zenflows.DB.{Page, Schema, Validate}
+alias Zenflows.VF.Agent
-@spec filter(Filter.params()) :: Filter.result()
-def filter(params) do
- case chgset(params) do
- %{valid?: true, changes: c} ->
- {:ok, Enum.reduce(c, Agent, &f(&2, &1))}
- %{valid?: false} = cset ->
- {:error, cset}
+@spec all(Page.t()) :: {:ok, Queryable.t()} | {:error, Changeset.t()}
+def all(%{filter: nil}), do: {:ok, Agent}
+def all(%{filter: params}) do
+ with {:ok, filters} <- all_validate(params) do
+ Enum.reduce(filters, Agent, &all_f(&2, &1))
end
end
-@spec f(Query.t(), {atom(), term()}) :: Query.t()
-defp f(q, {:name, v}),
- do: where(q, [x], ilike(x.name, ^"%#{Filter.escape_like(v)}%"))
-
-embedded_schema do
- field :name, :string
-end
+@spec all_f(Queryable.t(), {atom(), term()}) :: Queryable.t()
+defp all_f(q, {:name, v}),
+ do: where(q, [x], ilike(x.name, ^"%#{v}%"))
-@spec chgset(params()) :: Changeset.t()
-defp chgset(params) do
- %__MODULE__{}
+@spec all_validate(Schema.params())
+ :: {:ok, Changeset.data()} | {:error, Changeset.t()}
+defp all_validate(params) do
+ {%{}, %{name: :string}}
|> Changeset.cast(params, [:name])
|> Validate.name(:name)
+ |> Validate.escape_like(:name)
+ |> Changeset.apply_action(nil)
end
end
diff --git a/src/zenflows/vf/agent/resolv.ex b/src/zenflows/vf/agent/resolv.ex
@@ -16,8 +16,9 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Agent.Resolv do
-@moduledoc "Resolvers of Agents."
+@moduledoc false
+alias Zenflows.GQL.Connection
alias Zenflows.VF.Agent.Domain
def my_agent(_, %{context: %{req_user: user}}) do
@@ -29,7 +30,10 @@ def agent(params, _) do
end
def agents(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def images(agent, _, _) do
diff --git a/src/zenflows/vf/agent/type.ex b/src/zenflows/vf/agent/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Agent.Type do
-@moduledoc "GraphQL types of Agents."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/agent_relationship.ex b/src/zenflows/vf/agent_relationship.ex
@@ -23,10 +23,11 @@ with another.
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.VF.{
Agent,
AgentRelationshipRole,
- Validate,
}
@type t() :: %__MODULE__{
@@ -50,8 +51,8 @@ end
@cast @reqr ++ [:note] # in_scope_of
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/agent_relationship/domain.ex b/src/zenflows/vf/agent_relationship/domain.ex
@@ -18,16 +18,11 @@
defmodule Zenflows.VF.AgentRelationship.Domain do
@moduledoc "Domain logic of AgentRelationships."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.AgentRelationship
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, AgentRelationship.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -38,61 +33,112 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(AgentRelationship, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
+ :: AgentRelationship.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
end
-@spec create(params()) :: {:ok, AgentRelationship.t()} | {:error, chgset()}
+@spec all(Page.t()) :: {:ok, [AgentRelationship.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(AgentRelationship, page)}
+end
+
+@spec all!(Page.t()) :: [AgentRelationship.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
+end
+
+@spec create(Schema.params())
+ :: {:ok, AgentRelationship.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, AgentRelationship.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: ar}} -> {:ok, ar}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params())
- :: {:ok, AgentRelationship.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: AgentRelationship.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, AgentRelationship.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &AgentRelationship.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: ar}} -> {:ok, ar}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id())
- :: {:ok, AgentRelationship.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: AgentRelationship.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id())
+ :: {:ok, AgentRelationship.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, &(&1.one))
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: ar}} -> {:ok, ar}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+@spec delete!(Schema.id) :: AgentRelationship.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
@spec preload(AgentRelationship.t(), :subject | :object | :relationship)
:: AgentRelationship.t()
-def preload(rel, :subject) do
- Repo.preload(rel, :subject)
+def preload(rel, x) when x in ~w[subject object relationship]a do
+ Repo.preload(rel, x)
+end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :agent_relationship
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, AgentRelationship.changeset(params))
end
-def preload(rel, :object) do
- Repo.preload(rel, :object)
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &AgentRelationship.changeset(Map.fetch!(&1, "#{key}.one"), params))
end
-def preload(rel, :relationship) do
- Repo.preload(rel, :relationship)
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
end
end
diff --git a/src/zenflows/vf/agent_relationship/resolv.ex b/src/zenflows/vf/agent_relationship/resolv.ex
@@ -16,8 +16,9 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.AgentRelationship.Resolv do
-@moduledoc "Resolvers of AgentRelationships."
+@moduledoc false
+alias Zenflows.GQL.Connection
alias Zenflows.VF.{AgentRelationship, AgentRelationship.Domain}
def agent_relationship(params, _) do
@@ -25,7 +26,10 @@ def agent_relationship(params, _) do
end
def agent_relationships(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_agent_relationship(%{relationship: params}, _) do
diff --git a/src/zenflows/vf/agent_relationship/type.ex b/src/zenflows/vf/agent_relationship/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.AgentRelationship.Type do
-@moduledoc "GraphQL types of AgentRelationships."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/agent_relationship_role.ex b/src/zenflows/vf/agent_relationship_role.ex
@@ -23,7 +23,9 @@ such as member, trading partner.
use Zenflows.DB.Schema
-alias Zenflows.VF.{RoleBehavior, Validate}
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
+alias Zenflows.VF.RoleBehavior
@type t() :: %__MODULE__{
role_behavior: RoleBehavior.t() | nil,
@@ -44,8 +46,8 @@ end
@cast @reqr ++ ~w[role_behavior_id inverse_role_label note]a
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/agent_relationship_role/domain.ex b/src/zenflows/vf/agent_relationship_role/domain.ex
@@ -18,16 +18,11 @@
defmodule Zenflows.VF.AgentRelationshipRole.Domain do
@moduledoc "Domain logic of AgentRelationshipRoles."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.AgentRelationshipRole
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, AgentRelationshipRole.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -38,52 +33,109 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(AgentRelationshipRole, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: AgentRelationshipRole.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [AgentRelationshipRole.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(AgentRelationshipRole, page)}
+end
+
+@spec all!(Page.t()) :: [AgentRelationshipRole.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
end
-@spec create(params()) :: {:ok, AgentRelationshipRole.t()} | {:error, chgset()}
+@spec create(Schema.params()) :: {:ok, AgentRelationshipRole.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, AgentRelationshipRole.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: rr}} -> {:ok, rr}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params())
- :: {:ok, AgentRelationshipRole.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: AgentRelationshipRole.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, AgentRelationshipRole.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &AgentRelationshipRole.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: rr}} -> {:ok, rr}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id())
- :: {:ok, AgentRelationshipRole.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: AgentRelationshipRole.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id())
+ :: {:ok, AgentRelationshipRole.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, &(&1.one))
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: rr}} -> {:ok, rr}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+@spec delete!(Schema.id) :: AgentRelationshipRole.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
@spec preload(AgentRelationshipRole.t(), :role_behavior) :: AgentRelationshipRole.t()
def preload(rel_role, :role_behavior) do
Repo.preload(rel_role, :role_behavior)
end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :agent_relationship_role
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, AgentRelationshipRole.changeset(params))
+end
+
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &AgentRelationshipRole.changeset(Map.fetch!(&1, "#{key}.one"), params))
+end
+
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
+end
end
diff --git a/src/zenflows/vf/agent_relationship_role/resolv.ex b/src/zenflows/vf/agent_relationship_role/resolv.ex
@@ -16,8 +16,9 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.AgentRelationshipRole.Resolv do
-@moduledoc "Resolvers of AgentRelationshipRoles."
+@moduledoc false
+alias Zenflows.GQL.Connection
alias Zenflows.VF.{AgentRelationshipRole, AgentRelationshipRole.Domain}
def agent_relationship_role(params, _) do
@@ -25,7 +26,10 @@ def agent_relationship_role(params, _) do
end
def agent_relationship_roles(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_agent_relationship_role(%{agent_relationship_role: params}, _) do
diff --git a/src/zenflows/vf/agent_relationship_role/type.ex b/src/zenflows/vf/agent_relationship_role/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.AgentRelationshipRole.Type do
-@moduledoc "GraphQL types of AgentRelationshipRoles."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/agreement.ex b/src/zenflows/vf/agreement.ex
@@ -20,7 +20,8 @@ defmodule Zenflows.VF.Agreement do
use Zenflows.DB.Schema
-alias Zenflows.VF.Validate
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
@type t() :: %__MODULE__{
id: String.t(),
@@ -38,8 +39,8 @@ schema "vf_agreement" do
end
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/agreement/domain.ex b/src/zenflows/vf/agreement/domain.ex
@@ -18,16 +18,11 @@
defmodule Zenflows.VF.Agreement.Domain do
@moduledoc "Domain logic of Agreements."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.Agreement
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, Agreement.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -38,46 +33,103 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(Agreement, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: Agreement.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [Agreement.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(Agreement, page)}
+end
+
+@spec all!(Page.t()) :: [Agreement.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
end
-@spec create(params()) :: {:ok, Agreement.t()} | {:error, chgset()}
+@spec create(Schema.params()) :: {:ok, Agreement.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, Agreement.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: a}} -> {:ok, a}
+ {:ok, %{^key => value}} -> {:ok, value}
{:error, _, cset, _} -> {:error, cset}
end
end
-@spec update(id(), params())
- :: {:ok, Agreement.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: Agreement.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, Agreement.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &Agreement.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: a}} -> {:ok, a}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id()) :: {:ok, Agreement.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: Agreement.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id()) :: {:ok, Agreement.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, &(&1.one))
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: a}} -> {:ok, a}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+
+@spec delete!(Schema.id) :: Agreement.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :agreement
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, Agreement.changeset(params))
+end
+
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &Agreement.changeset(Map.fetch!(&1, "#{key}.one"), params))
+end
+
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
+end
end
diff --git a/src/zenflows/vf/agreement/resolv.ex b/src/zenflows/vf/agreement/resolv.ex
@@ -16,8 +16,9 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Agreement.Resolv do
-@moduledoc "Resolvers of Agreements."
+@moduledoc false
+alias Zenflows.GQL.Connection
alias Zenflows.VF.Agreement.Domain
def agreement(params, _info) do
@@ -25,7 +26,10 @@ def agreement(params, _info) do
end
def agreements(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_agreement(%{agreement: params}, _info) do
diff --git a/src/zenflows/vf/agreement/type.ex b/src/zenflows/vf/agreement/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Agreement.Type do
-@moduledoc "GraphQL types of Agreements."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/appreciation.ex b/src/zenflows/vf/appreciation.ex
@@ -24,7 +24,9 @@ gift economy.
use Zenflows.DB.Schema
-alias Zenflows.VF.{EconomicEvent, Validate}
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
+alias Zenflows.VF.EconomicEvent
@type t() :: %__MODULE__{
appreciation_of: EconomicEvent.t(),
@@ -43,8 +45,8 @@ end
@cast @reqr ++ [:note]
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/claim.ex b/src/zenflows/vf/claim.ex
@@ -23,6 +23,8 @@ received.
"""
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.VF.{
Action,
Agent,
@@ -30,7 +32,6 @@ alias Zenflows.VF.{
Measure,
ResourceSpecification,
Unit,
- Validate,
}
@type t() :: %__MODULE__{
@@ -79,8 +80,8 @@ end
]a # in_scope_of
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/commitment.ex b/src/zenflows/vf/commitment.ex
@@ -23,6 +23,8 @@ agent.
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.VF.{
Action,
Agent,
@@ -34,7 +36,6 @@ alias Zenflows.VF.{
ResourceSpecification,
SpatialThing,
Unit,
- Validate,
}
@type t() :: %__MODULE__{
@@ -102,13 +103,15 @@ end
]a # in_scope_of_id
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
- |> datetime_check()
- |> resource_check()
+ |> Validate.exist_or([:has_point_in_time, :has_beginning, :has_end, :due])
+ |> Validate.exist_nand([:has_point_in_time, :has_beginning])
+ |> Validate.exist_nand([:has_point_in_time, :has_end])
+ |> Validate.exist_xor([:resource_conforms_to_id, :resource_inventoried_as_id], method: :both)
|> Validate.note(:note)
|> Validate.class(:resource_classified_as)
|> Measure.cast(:effort_quantity)
@@ -123,109 +126,4 @@ def chgset(schema \\ %__MODULE__{}, params) do
|> Changeset.assoc_constraint(:at_location)
|> Changeset.assoc_constraint(:clause_of)
end
-
-# Validate that either :has_point_in_time, :has_beginning, :has_end,
-# or :due is provided and that :has_point_in_time and (:has_beginning
-# and/or :has_end) are mutually exclusive.
-@spec datetime_check(Changeset.t()) :: Changeset.t()
-defp datetime_check(cset) do
- # credo:disable-for-previous-line Credo.Check.Refactor.CyclomaticComplexity
-
- {data_point, chng_point, field_point} =
- case Changeset.fetch_field(cset, :has_point_in_time) do
- {:data, x} -> {x, nil, x}
- {:changes, x} -> {nil, x, x}
- end
- {data_begin, chng_begin, field_begin} =
- case Changeset.fetch_field(cset, :has_beginning) do
- {:data, x} -> {x, nil, x}
- {:changes, x} -> {nil, x, x}
- end
- {data_end, chng_end, field_end} =
- case Changeset.fetch_field(cset, :has_end) do
- {:data, x} -> {x, nil, x}
- {:changes, x} -> {nil, x, x}
- end
- field_due = Changeset.get_field(cset, :due)
-
- cond do
- data_point && chng_begin ->
- msg = "has_beginning is not allowed in this record"
- Changeset.add_error(cset, :has_beginning, msg)
-
- data_point && chng_end ->
- msg = "has_end is not allowed in this record"
- Changeset.add_error(cset, :has_end, msg)
-
- (data_begin || data_end) && chng_point ->
- msg = "has_point_in_time is not allowed in this record"
- Changeset.add_error(cset, :has_point_in_time, msg)
-
- chng_point && chng_begin ->
- msg = "has_point_in_time and has_beginning are mutually exclusive"
-
- cset
- |> Changeset.add_error(:has_point_in_time, msg)
- |> Changeset.add_error(:has_beginning, msg)
-
- chng_point && chng_end ->
- msg = "has_point_in_time and has_end are mutually exclusive"
-
- cset
- |> Changeset.add_error(:has_point_in_time, msg)
- |> Changeset.add_error(:has_end, msg)
-
- field_point || field_begin || field_end || field_due ->
- cset
-
- true ->
- msg = "hasBeginning or hasEnd or hasPointInTime or due is required"
-
- cset
- |> Changeset.add_error(:has_point_in_time, msg)
- |> Changeset.add_error(:has_beginning, msg)
- |> Changeset.add_error(:has_end, msg)
- |> Changeset.add_error(:due, msg)
- end
-end
-
-# Validate mutual exclusivity of having an actual resource or its
-# specification.
-# In other words, forbid :resource_conforms_to and
-# :resource_inventoried_as to be provided at the same time.
-@spec resource_check(Changeset.t()) :: Changeset.t()
-defp resource_check(cset) do
- # credo:disable-for-previous-line Credo.Check.Refactor.CyclomaticComplexity
-
- {data_res_con, chng_res_con} =
- case Changeset.fetch_field(cset, :resource_conforms_to_id) do
- {:data, x} -> {x, nil}
- {:changes, x} -> {nil, x}
- end
- {data_res_inv, chng_res_inv} =
- case Changeset.fetch_field(cset, :resource_inventoried_as_id) do
- {:data, x} -> {x, nil}
- {:changes, x} -> {nil, x}
- end
-
- cond do
- data_res_con && chng_res_inv ->
- msg = "resource_inventoried_as is not allowed in this record"
- Changeset.add_error(cset, :resource_inventoried_as_id, msg)
-
- data_res_inv && chng_res_con ->
- msg = "resource_conforms_to is not allowed in this record"
- Changeset.add_error(cset, :resource_conforms_to_id, msg)
-
- chng_res_con && chng_res_inv ->
- msg = "resource_conforms_to and resource_inventoried_as are mutually exclusive"
-
- cset
- |> Changeset.add_error(:resource_conforms_to_id, msg)
- |> Changeset.add_error(:resource_inventoried_as_id, msg)
-
- true ->
- cset
- end
-end
end
diff --git a/src/zenflows/vf/duration.ex b/src/zenflows/vf/duration.ex
@@ -22,6 +22,8 @@ Represents an interval between two DateTime values.
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.Schema
alias Zenflows.VF.TimeUnitEnum
@type t() :: %__MODULE__{
@@ -44,25 +46,23 @@ and `Zenflows.VF.ScenarioDefinition` modules.
def cast(cset, key) do
case Changeset.fetch_change(cset, key) do
{:ok, params} ->
- case chgset(params) do
+ case changeset(params) do
%{valid?: true} = cset_dur ->
cset
- |> Changeset.put_change(field_unit_type(key),
+ |> Changeset.put_change(:"#{key}_unit_type",
Changeset.fetch_change!(cset_dur, :unit_type))
- |> Changeset.put_change(field_numeric_duration(key),
+ |> Changeset.put_change(:"#{key}_numeric_duration",
Changeset.fetch_change!(cset_dur, :numeric_duration))
-
cset_dur ->
cset_dur.errors
|> Enum.reduce(cset, fn {field, {msg, _opts}}, acc ->
Changeset.add_error(acc, key, "#{field}: #{msg}")
end)
end
-
:error ->
# Ecto seems to convert the params' keys to string
# whether they were originally string or atom.
- strkey = Atom.to_string(key)
+ strkey = "#{key}"
case cset.params do
# If, for example, `key` is
# `:has_duration`, and it's set to `nil`,
@@ -70,9 +70,8 @@ def cast(cset, key) do
# `nil` as well.
%{^strkey => nil} ->
cset
- |> Changeset.force_change(field_unit_type(key), nil)
- |> Changeset.force_change(field_numeric_duration(key), nil)
-
+ |> Changeset.force_change(:"#{key}_unit_type", nil)
+ |> Changeset.force_change(:"#{key}_numeric_duration", nil)
_ ->
cset
end
@@ -87,29 +86,19 @@ as a %Duration{} struct. Useful for GraphQL types as can be seen in
@spec preload(Schema.t(), atom()) :: Schema.t()
def preload(schema, key) do
%{schema | key => %__MODULE__{
- unit_type: Map.get(schema, field_unit_type(key)),
- numeric_duration: Map.get(schema, field_numeric_duration(key)),
+ unit_type: Map.get(schema, :"#{key}_unit_type"),
+ numeric_duration: Map.get(schema, :"#{key}_numeric_duration"),
}}
end
@cast ~w[unit_type numeric_duration]a
@reqr @cast
-@spec chgset(params()) :: Changeset.t()
-defp chgset(params) do
+@spec changeset(Schema.params()) :: Changeset.t()
+defp changeset(params) do
%__MODULE__{}
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
|> Changeset.validate_number(:numeric_duration, greater_than_or_equal_to: 0)
end
-
-@spec field_unit_type(atom()) :: atom()
-defp field_unit_type(key) do
- String.to_existing_atom("#{key}_unit_type")
-end
-
-@spec field_numeric_duration(atom()) :: atom()
-defp field_numeric_duration(key) do
- String.to_existing_atom("#{key}_numeric_duration")
-end
end
diff --git a/src/zenflows/vf/duration/type.ex b/src/zenflows/vf/duration/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Duration.Type do
-@moduledoc "GraphQL types of Durations."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/economic_event.ex b/src/zenflows/vf/economic_event.ex
@@ -24,6 +24,8 @@ resource.
"""
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.VF.{
Action,
Agent,
@@ -35,7 +37,6 @@ alias Zenflows.VF.{
ResourceSpecification,
SpatialThing,
Unit,
- Validate,
}
@type t() :: %__MODULE__{
@@ -100,18 +101,15 @@ end
# insert changeset
@doc false
-@spec chgset(params()) :: Changeset.t()
-def chgset(params) do
+@spec changeset(Schema.params()) :: Changeset.t()
+def changeset(params) do
%__MODULE__{}
|> Changeset.cast(params, @insert_cast)
|> Changeset.validate_required(@insert_reqr)
- |> datetime_check()
- |> case do
- %{valid?: true, changes: %{action_id: action}} = cset ->
- do_chgset(action, Changeset.apply_changes(cset), params)
- cset ->
- cset
- end
+ |> Validate.exist_or([:has_point_in_time, :has_beginning, :has_end])
+ |> Validate.exist_nand([:has_point_in_time, :has_beginning])
+ |> Validate.exist_nand([:has_point_in_time, :has_end])
+ |> do_changeset()
|> Validate.uri(:agreed_in)
|> Validate.note(:note)
|> Validate.class(:resource_classified_as)
@@ -128,50 +126,47 @@ def chgset(params) do
|> Changeset.assoc_constraint(:triggered_by)
end
-@spec do_chgset(Action.ID.t(), Schema.t(), params()) :: Changeset.t()
-defp do_chgset("raise", schema, params) do
- schema
- |> Changeset.cast(params, ~w[
+@spec do_changeset(Changeset.t()) :: Changeset.t()
+defp do_changeset(%{valid?: false} = cset), do: cset
+defp do_changeset(%{changes: %{action_id: "raise"}} = cset) do
+ cset
+ |> Changeset.cast(cset.params, ~w[
resource_conforms_to_id resource_inventoried_as_id
resource_classified_as resource_quantity to_location_id
]a)
|> Changeset.validate_required([:resource_quantity])
|> Measure.cast(:resource_quantity)
- |> require_agents_same()
- |> xor_required(:resource_conforms_to_id, :resource_inventoried_as_id)
+ |> Validate.value_eq([:provider_id, :receiver_id])
+ |> Validate.exist_xor([:resource_conforms_to_id, :resource_inventoried_as_id])
end
-
-defp do_chgset("produce", schema, params) do
- schema
- |> Changeset.cast(params, ~w[
+defp do_changeset(%{changes: %{action_id: "produce"}} = cset) do
+ cset
+ |> Changeset.cast(cset.params, ~w[
output_of_id resource_conforms_to_id resource_inventoried_as_id
resource_classified_as resource_quantity to_location_id
]a)
|> Changeset.validate_required(~w[output_of_id resource_quantity]a)
|> Measure.cast(:resource_quantity)
- |> require_agents_same()
- |> xor_required(:resource_conforms_to_id, :resource_inventoried_as_id)
+ |> Validate.value_eq([:provider_id, :receiver_id])
+ |> Validate.exist_xor([:resource_conforms_to_id, :resource_inventoried_as_id])
end
-
-defp do_chgset("lower", schema, params) do
- schema
- |> Changeset.cast(params, ~w[resource_inventoried_as_id resource_quantity]a)
+defp do_changeset(%{changes: %{action_id: "lower"}} = cset) do
+ cset
+ |> Changeset.cast(cset.params, ~w[resource_inventoried_as_id resource_quantity]a)
|> Changeset.validate_required(~w[resource_inventoried_as_id resource_quantity]a)
- |> require_agents_same()
+ |> Validate.value_eq([:provider_id, :receiver_id])
|> Measure.cast(:resource_quantity)
end
-
-defp do_chgset("consume", schema, params) do
- schema
- |> Changeset.cast(params, ~w[input_of_id resource_inventoried_as_id resource_quantity]a)
+defp do_changeset(%{changes: %{action_id: "consume"}} = cset) do
+ cset
+ |> Changeset.cast(cset.params, ~w[input_of_id resource_inventoried_as_id resource_quantity]a)
|> Changeset.validate_required(~w[input_of_id resource_inventoried_as_id resource_quantity]a)
|> Measure.cast(:resource_quantity)
- |> require_agents_same()
+ |> Validate.value_eq([:provider_id, :receiver_id])
end
-
-defp do_chgset("use", schema, params) do
- schema
- |> Changeset.cast(params, ~w[
+defp do_changeset(%{changes: %{action_id: "use"}} = cset) do
+ cset
+ |> Changeset.cast(cset.params, ~w[
input_of_id effort_quantity
resource_inventoried_as_id resource_conforms_to_id
resource_quantity
@@ -179,220 +174,108 @@ defp do_chgset("use", schema, params) do
|> Changeset.validate_required(~w[input_of_id effort_quantity]a)
|> Measure.cast(:effort_quantity)
|> Measure.cast(:resource_quantity)
- |> xor_required(:resource_inventoried_as_id, :resource_conforms_to_id)
+ |> Validate.exist_xor([:resource_inventoried_as_id, :resource_conforms_to_id])
end
-
-defp do_chgset("work", schema, params) do
- schema
- |> Changeset.cast(params, ~w[input_of_id effort_quantity resource_conforms_to_id]a)
+defp do_changeset(%{changes: %{action_id: "work"}} = cset) do
+ cset
+ |> Changeset.cast(cset.params, ~w[input_of_id effort_quantity resource_conforms_to_id]a)
|> Changeset.validate_required(~w[input_of_id effort_quantity resource_conforms_to_id]a)
|> Measure.cast(:effort_quantity)
end
-
-defp do_chgset("cite", schema, params) do
- schema
- |> Changeset.cast(params, ~w[
+defp do_changeset(%{changes: %{action_id: "cite"}} = cset) do
+ cset
+ |> Changeset.cast(cset.params, ~w[
input_of_id resource_quantity
resource_inventoried_as_id resource_conforms_to_id
]a)
|> Changeset.validate_required(~w[input_of_id resource_quantity]a)
|> Measure.cast(:resource_quantity)
- |> xor_required(:resource_inventoried_as_id, :resource_conforms_to_id)
+ |> Validate.exist_xor([:resource_inventoried_as_id, :resource_conforms_to_id])
end
-
-defp do_chgset("deliverService", schema, params) do
- schema
- |> Changeset.cast(params, ~w[input_of_id output_of_id resource_conforms_to_id]a)
+defp do_changeset(%{changes: %{action_id: "deliverService"}} = cset) do
+ cset
+ |> Changeset.cast(cset.params, ~w[input_of_id output_of_id resource_conforms_to_id]a)
|> Changeset.validate_required(~w[resource_conforms_to_id]a)
- |> require_different_procs()
+ |> Validate.value_ne([:input_of_id, :output_of_id])
end
-
-defp do_chgset("pickup", schema, params) do
- schema
- |> Changeset.cast(params, ~w[input_of_id resource_quantity resource_inventoried_as_id]a)
+defp do_changeset(%{changes: %{action_id: "pickup"}} = cset) do
+ cset
+ |> Changeset.cast(cset.params, ~w[input_of_id resource_quantity resource_inventoried_as_id]a)
|> Changeset.validate_required(~w[input_of_id resource_quantity resource_inventoried_as_id]a)
|> Measure.cast(:resource_quantity)
- |> require_agents_same()
+ |> Validate.value_eq([:provider_id, :receiver_id])
end
-
-defp do_chgset("dropoff", schema, params) do
- schema
- |> Changeset.cast(params, ~w[output_of_id resource_quantity resource_inventoried_as_id to_location_id]a)
+defp do_changeset(%{changes: %{action_id: "dropoff"}} = cset) do
+ cset
+ |> Changeset.cast(cset.params, ~w[output_of_id resource_quantity resource_inventoried_as_id to_location_id]a)
|> Changeset.validate_required(~w[output_of_id resource_quantity resource_inventoried_as_id]a)
|> Measure.cast(:resource_quantity)
- |> require_agents_same()
+ |> Validate.value_eq([:provider_id, :receiver_id])
end
-
-defp do_chgset("accept", schema, params) do
- schema
- |> Changeset.cast(params, ~w[input_of_id resource_quantity resource_inventoried_as_id]a)
+defp do_changeset(%{changes: %{action_id: "accept"}} = cset) do
+ cset
+ |> Changeset.cast(cset.params, ~w[input_of_id resource_quantity resource_inventoried_as_id]a)
|> Changeset.validate_required(~w[input_of_id resource_quantity resource_inventoried_as_id]a)
|> Measure.cast(:resource_quantity)
- |> require_agents_same()
+ |> Validate.value_eq([:provider_id, :receiver_id])
end
-
-defp do_chgset("modify", schema, params) do
- schema
- |> Changeset.cast(params, ~w[output_of_id resource_quantity resource_inventoried_as_id]a)
+defp do_changeset(%{changes: %{action_id: "modify"}} = cset) do
+ cset
+ |> Changeset.cast(cset.params, ~w[output_of_id resource_quantity resource_inventoried_as_id]a)
|> Changeset.validate_required(~w[output_of_id resource_quantity resource_inventoried_as_id]a)
|> Measure.cast(:resource_quantity)
- |> require_agents_same()
+ |> Validate.value_eq([:provider_id, :receiver_id])
end
-
-defp do_chgset("combine", schema, params) do
- schema
- |> Changeset.cast(params, ~w[]a)
+defp do_changeset(%{changes: %{action_id: "combine"}} = cset) do
+ cset
+ |> Changeset.cast(cset.params, ~w[]a)
|> Changeset.validate_required(~w[]a)
end
-
-defp do_chgset("separate", schema, params) do
- schema
- |> Changeset.cast(params, ~w[]a)
+defp do_changeset(%{changes: %{action_id: "separate"}} = cset) do
+ cset
+ |> Changeset.cast(cset.params, ~w[]a)
|> Changeset.validate_required(~w[]a)
end
-
-defp do_chgset("transferAllRights", schema, params) do
- schema
- |> Changeset.cast(params, ~w[
+defp do_changeset(%{changes: %{action_id: "transferAllRights"}} = cset) do
+ cset
+ |> Changeset.cast(cset.params, ~w[
resource_inventoried_as_id to_resource_inventoried_as_id
resource_quantity resource_classified_as
]a)
|> Changeset.validate_required(~w[resource_inventoried_as_id resource_quantity]a)
|> Measure.cast(:resource_quantity)
end
-
-defp do_chgset(action, schema, params) when action in ~w[transferCustody transfer] do
- schema
- |> Changeset.cast(params, ~w[
+defp do_changeset(%{changes: %{action_id: id}} = cset)
+ when id in ~w[transferCustody transfer] do
+ cset
+ |> Changeset.cast(cset.params, ~w[
resource_inventoried_as_id to_resource_inventoried_as_id resource_quantity
to_location_id resource_classified_as
]a)
|> Changeset.validate_required(~w[resource_inventoried_as_id resource_quantity]a)
|> Measure.cast(:resource_quantity)
end
-
-defp do_chgset("move", schema, params) do
- schema
- |> Changeset.cast(params, ~w[
+defp do_changeset(%{changes: %{action_id: "move"}} = cset) do
+ cset
+ |> Changeset.cast(cset.params, ~w[
resource_inventoried_as_id to_resource_inventoried_as_id resource_quantity
to_location_id resource_classified_as
]a)
|> Changeset.validate_required(~w[resource_inventoried_as_id resource_quantity]a)
|> Measure.cast(:resource_quantity)
- |> require_agents_same()
-end
-
-# Require that `:provider` and `:receiver` be the same agents.
-@spec require_agents_same(Changeset.t()) :: Changeset.t()
-defp require_agents_same(cset) do
- prov = cset.data.provider_id
- recv = cset.data.receiver_id
-
- # since provider and receiver is required, there's no nil case to care
- if prov != recv do
- msg = "agents must be the same"
- cset
- |> Changeset.add_error(:provider_id, msg)
- |> Changeset.add_error(:receiver_id, msg)
- else
- cset
- end
-end
-
-# Require either of the given fields `a` xor `b`.
-@spec xor_required(Changeset.t(), atom(), atom()) :: Changeset.t()
-defp xor_required(cset, a, b) do
- x = Changeset.get_change(cset, a)
- y = Changeset.get_change(cset, b)
-
- if (x && !y) || (!x && y) do
- cset
- else
- msg = "these are mutually exclusive and exactly one must be provided"
-
- cset
- |> Changeset.add_error(a, msg)
- |> Changeset.add_error(b, msg)
- end
-end
-
-# Require that `:input_of` and/or `:output_of` is required, and
-# that they are not the same.
-@spec require_different_procs(Changeset.t()) :: Changeset.t()
-defp require_different_procs(cset) do
- input = Changeset.get_change(cset, :input_of_id)
- output = Changeset.get_change(cset, :output_of_id)
-
- cond do
- input != nil and output != nil and input == output ->
- msg = "must have different processes"
-
- cset
- |> Changeset.add_error(:input_of_id, msg)
- |> Changeset.add_error(:output_of_id, msg)
-
- input == nil and output == nil ->
- msg = "either of these must not be blank"
-
- cset
- |> Changeset.add_error(:input_of_id, msg)
- |> Changeset.add_error(:output_of_id, msg)
-
- true ->
- cset
- end
+ |> Validate.value_eq([:provider_id, :receiver_id])
end
@update_cast ~w[note agreed_in realization_of_id triggered_by_id]a
# update changeset
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema, params) do
schema
|> Changeset.cast(params, @update_cast)
|> Validate.note(:note)
|> Changeset.assoc_constraint(:realization_of)
|> Changeset.assoc_constraint(:triggered_by)
end
-
-# Validate datetime mutual exclusivity and requirements.
-# In other words, require one of these combinations to be provided:
-# * only :has_point_in_time
-# * only :has_beginning and/or :has_end
-#
-# This is only for inserting changeset.
-@spec datetime_check(Changeset.t()) :: Changeset.t()
-defp datetime_check(cset) do
- point = Changeset.get_change(cset, :has_point_in_time)
- begin = Changeset.get_change(cset, :has_beginning)
- endd = Changeset.get_change(cset, :has_end)
-
- cond do
- point && begin ->
- msg = "'has point in time' and 'has beginning' are mutually exclusive"
-
- cset
- |> Changeset.add_error(:has_point_in_time, msg)
- |> Changeset.add_error(:has_beginning, msg)
-
- point && endd ->
- msg = "'has point in time' and 'has end' are mutually exclusive"
-
- cset
- |> Changeset.add_error(:has_point_in_time, msg)
- |> Changeset.add_error(:has_end, msg)
-
- point || begin || endd ->
- cset
-
- true ->
- msg = "'has point in time', 'has beginning', or 'has end' is requried"
-
- cset
- |> Changeset.add_error(:has_beginning, msg)
- |> Changeset.add_error(:has_end, msg)
- |> Changeset.add_error(:has_point_in_time, msg)
- end
-end
end
diff --git a/src/zenflows/vf/economic_event/domain.ex b/src/zenflows/vf/economic_event/domain.ex
@@ -21,7 +21,7 @@ defmodule Zenflows.VF.EconomicEvent.Domain do
import Ecto.Query
alias Ecto.{Changeset, Multi}
-alias Zenflows.DB.{Paging, Repo}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.{
Action,
EconomicEvent,
@@ -29,12 +29,7 @@ alias Zenflows.VF.{
Measure,
}
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, EconomicEvent.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -45,52 +40,110 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(EconomicEvent, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
+ :: EconomicEvent.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [EconomicEvent.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(EconomicEvent, page)}
+end
+
+@spec all!(Page.t()) :: [EconomicEvent.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
end
-@spec create(params(), params()) :: {:ok, EconomicEvent.t(), EconomicResource.t(), nil}
- | {:ok, EconomicEvent.t(), nil, EconomicResource.t()}
- | {:ok, EconomicEvent.t()} | {:error, String.t() | chgset()}
-def create(evt_params, res_params) do
+@spec create(Schema.params(), nil | Schema.params())
+ :: {:ok, EconomicEvent.t()} | {:error, String.t() | Changeset.t()}
+def create(evt_params, res_params \\ nil) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:created_evt, EconomicEvent.chgset(evt_params))
- |> Multi.merge(fn %{created_evt: evt} ->
- handle_multi(evt.action_id, evt, res_params || %{}) # since it can be empty
- end)
+ |> multi_insert(evt_params, res_params)
|> Repo.transaction()
|> case do
- {:ok, %{updated_evt: evt} = map} ->
- if map[:eco_res] || map[:to_eco_res] do
- {:ok, evt, map[:eco_res], map[:to_eco_res]}
- else
- {:ok, evt}
- end
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
+ end
+end
- {:ok, %{created_evt: evt}} ->
- {:ok, evt}
+@spec create!(Schema.params(), nil | Schema.params()) :: EconomicEvent.t()
+def create!(evt_params, res_params \\ nil) do
+ {:ok, value} = create(evt_params, res_params)
+ value
+end
- {:error, _, msg_or_cset, _} ->
- {:error, msg_or_cset}
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, EconomicEvent.t()} | {:error, String.t() | Changeset.t()}
+def update(id, params) do
+ key = multi_key()
+ Multi.new()
+ |> multi_update(id, params)
+ |> Repo.transaction()
+ |> case do
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+@spec update!(Schema.id(), Schema.params()) :: EconomicEvent.t()
+def update!(id, params) do
+ # `__MODULE__` because it confilicts with `import Ecto.Query`
+ {:ok, value} = __MODULE__.update(id, params)
+ value
+end
+
+@spec preload(EconomicEvent.t(), :action | :input_of | :output_of
+ | :provider | :receiver
+ | :resource_inventoried_as | :to_resource_inventoried_as
+ | :resource_conforms_to | :resource_quantity | :effort_quantity
+ | :to_location | :at_location | :realization_of | :triggered_by)
+ :: EconomicEvent.t()
+def preload(eco_evt, x) when x in ~w[
+ input_of output_of provider receiver
+ resource_inventoried_as to_resource_inventoried_as
+ resource_conforms_to to_location at_location realization_of
+ triggered_by
+]a do
+ Repo.preload(eco_evt, x)
+end
+def preload(eco_evt, :action),
+ do: Action.preload(eco_evt, :action)
+def preload(eco_evt, x) when x in ~w[resource_quantity effort_quantity]a,
+ do: Measure.preload(eco_evt, x)
+
+@spec multi_key() :: atom()
+def multi_key(), do: :economic_event
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params(), nil | Schema.params())
+ :: Multi.t()
+def multi_insert(m, key \\ multi_key(), evt_params, res_params) do
+ m
+ |> Multi.insert("#{key}.created", EconomicEvent.changeset(evt_params))
+ |> Multi.merge(&handle_insert(key, Map.fetch!(&1, "#{key}.created"), res_params))
+end
+
# Handle the part after the event is created. These clauses deal with
# validations, creation of resources, and any other side-effects.
#
-# If they return a multi named `:updated_evt`, the value (assuming
-# it is a event struct) of it will be passed back as `{:ok, value}`.
-#
-# They can optionally return a multi named `:eco_res` (assuming
-# it is a resource struct) that can be used for a tiny optimization
-# on the resolver (when create a resource, fetch it, etc.).
-@spec handle_multi(Action.ID.t(), EconomicEvent.t(), params() | nil) :: Multi.t()
-defp handle_multi(action_id, evt, res_params) when action_id in ["raise", "produce"] do
+# It either returns the given `evt` as it is under the name `key`,
+# or updates `evt` and returns it under the name `key`.
+@spec handle_insert(term(), EconomicEvent.t(), nil | Schema.params()) :: Multi.t()
+defp handle_insert(key, %{action_id: action_id} = evt, res_params)
+ when action_id in ["raise", "produce"] do
cond do
evt.resource_conforms_to_id != nil ->
res_params =
- res_params
+ (res_params || %{})
|> Map.put(:primary_accountable_id, evt.receiver_id)
|> Map.put(:custodian_id, evt.receiver_id)
|> Map.put(:conforms_to_id, evt.resource_conforms_to_id)
@@ -102,14 +155,12 @@ defp handle_multi(action_id, evt, res_params) when action_id in ["raise", "produ
|> Map.put(:classified_as, evt.resource_classified_as)
Multi.new()
- |> Multi.insert(:eco_res, EconomicResource.chgset(res_params))
- |> Multi.update(:updated_evt, fn %{eco_res: res} ->
- Changeset.change(evt, resource_inventoried_as_id: res.id)
- end)
-
+ |> EconomicResource.Domain.multi_insert("#{key}.eco_res", res_params)
+ |> Multi.update(key, &Changeset.change(evt,
+ resource_inventoried_as_id: &1 |> Map.fetch!("#{key}.eco_res") |> Map.fetch!(:id)))
evt.resource_inventoried_as_id != nil ->
Multi.new()
- |> Multi.run(:checks, fn repo, _ ->
+ |> Multi.run("#{key}.checks", fn repo, _ ->
fields = ~w[
primary_accountable_id custodian_id
accounting_quantity_has_unit_id
@@ -129,30 +180,27 @@ defp handle_multi(action_id, evt, res_params) when action_id in ["raise", "produ
cond do
evt.provider_id != res.primary_accountable_id or evt.provider_id != res.custodian_id ->
{:error, "you don't have ownership over this resource"}
-
evt.resource_quantity_has_unit_id != res.accounting_quantity_has_unit_id ->
{:error, "the unit of resource quantity must match with the unit of this resource"}
-
res.contained? ->
{:error, "you can't #{action_id} into a contained resource"}
-
res.container? ->
{:error, "you can't #{action_id} into a container resource"}
-
true ->
{:ok, nil}
end
end)
- |> Multi.update_all(:inc, where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
+ |> Multi.update_all("#{key}.inc", where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
accounting_quantity_has_numerical_value: evt.resource_quantity_has_numerical_value,
onhand_quantity_has_numerical_value: evt.resource_quantity_has_numerical_value,
])
+ |> Multi.put(key, evt)
end
end
-
-defp handle_multi(action_id, evt, _) when action_id in ["lower", "consume"] do
+defp handle_insert(key, %{action_id: action_id} = evt, _)
+ when action_id in ["lower", "consume"] do
Multi.new()
- |> Multi.run(:checks, fn repo, _ ->
+ |> Multi.run("#{key}.checks", fn repo, _ ->
fields = ~w[
primary_accountable_id custodian_id
accounting_quantity_has_unit_id
@@ -172,38 +220,33 @@ defp handle_multi(action_id, evt, _) when action_id in ["lower", "consume"] do
cond do
evt.provider_id != res.primary_accountable_id or evt.provider_id != res.custodian_id ->
{:error, "you don't have ownership over this resource"}
-
evt.resource_quantity_has_unit_id != res.accounting_quantity_has_unit_id ->
{:error, "the unit of resource quantity must match with the unit of this resource"}
-
res.contained? -> # TODO: study combine-separate
{:error, "you can't #{action_id} a contained resource"}
-
res.container? ->
{:error, "you can't #{action_id} a container resource"}
-
true ->
{:ok, nil}
end
end)
- |> Multi.update_all(:dec, where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
+ |> Multi.update_all("#{key}.dec", where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
accounting_quantity_has_numerical_value: -evt.resource_quantity_has_numerical_value,
onhand_quantity_has_numerical_value: -evt.resource_quantity_has_numerical_value,
])
+ |> Multi.put(key, evt)
end
-
-defp handle_multi(action_id, _evt, _) when action_id in ~w[work deliverService] do
- Multi.new()
+defp handle_insert(key, %{action_id: action_id} = evt, _)
+ when action_id in ~w[work deliverService] do
+ Multi.put(Multi.new(), key, evt)
end
-
-defp handle_multi("use", evt, _) do
+defp handle_insert(key, %{action_id: "use"} = evt, _) do
cond do
evt.resource_conforms_to_id != nil ->
- Multi.new()
-
+ Multi.put(Multi.new(), key, evt)
evt.resource_inventoried_as_id != nil ->
Multi.new()
- |> Multi.run(:checks, fn repo, _ ->
+ |> Multi.run("#{key}.checks", fn repo, _ ->
fields = if evt.resource_quantity != nil,
do: [:accounting_quantity_has_unit_id],
else: []
@@ -223,28 +266,24 @@ defp handle_multi("use", evt, _) do
cond do
evt.resource_quantity && (evt.resource_quantity_has_unit_id != res.accounting_quantity_has_unit_id) ->
{:error, "the unit of resource quantity must match with the unit of this resource"}
-
res.contained? -> # TODO: study combine-separate
{:error, "you can't use a contained resource"}
-
res.container? ->
{:error, "you can't use a container resource"}
-
true ->
{:ok, nil}
end
end)
+ |> Multi.put(key, evt)
end
end
-
-defp handle_multi("cite", evt, _) do
+defp handle_insert(key, %{action_id: "cite"} = evt, _) do
cond do
evt.resource_conforms_to_id != nil ->
- Multi.new()
-
+ Multi.put(Multi.new(), key, evt)
evt.resource_inventoried_as_id != nil ->
Multi.new()
- |> Multi.run(:checks, fn repo, _ ->
+ |> Multi.run("#{key}.checks", fn repo, _ ->
res = from(
r in EconomicResource,
where: [id: ^evt.resource_inventoried_as_id],
@@ -259,23 +298,20 @@ defp handle_multi("cite", evt, _) do
cond do
evt.resource_quantity_has_unit_id != res.accounting_quantity_has_unit_id ->
{:error, "the unit of resource quantity must match with the unit of this resource"}
-
res.contained? -> # TODO: study combine-separate
{:error, "you can't cite a contained resource"}
-
res.container? ->
{:error, "you can't cite a container resource"}
-
true ->
{:ok, nil}
end
end)
+ |> Multi.put(key, evt)
end
end
-
-defp handle_multi("pickup", evt, _) do
+defp handle_insert(key, %{action_id: "pickup"} = evt, _) do
Multi.new()
- |> Multi.run(:checks, fn repo, _ ->
+ |> Multi.run("#{key}.checks", fn repo, _ ->
fields = ~w[
custodian_id
onhand_quantity_has_numerical_value onhand_quantity_has_unit_id
@@ -289,26 +325,22 @@ defp handle_multi("pickup", evt, _) do
)
|> repo.one!()
- single_ref? = fn ->
+ not_single_ref? = fn ->
where(EconomicEvent, [e],
e.resource_inventoried_as_id == ^evt.resource_inventoried_as_id
and e.id != ^evt.id
and e.action_id == "pickup"
and e.input_of_id == ^evt.input_of_id)
|> repo.exists?()
- |> Kernel.not()
end
cond do
evt.provider_id != res.custodian_id ->
{:error, "you don't have custody over this resource"}
-
res.contained? -> # TODO: study combine-separate
{:error, "you can't pickup a contained resource"}
-
evt.resource_quantity_has_unit_id != res.onhand_quantity_has_unit_id ->
{:error, "the unit of resource quantity must match with the unit of this resource"}
-
# This also handles the requirement that the
# resource's onhand quantity must be positive.
# This is guranteed because of the check below
@@ -316,19 +348,18 @@ defp handle_multi("pickup", evt, _) do
# of events must be positive.
evt.resource_quantity_has_numerical_value != res.onhand_quantity_has_numerical_value ->
{:error, "the pickup events need to fully pickup the resource"}
-
- not single_ref?.() ->
- {:error, "no more than one pickup event in the same process, referring to the same resource is allowed"}
-
+ not_single_ref?.() ->
+ {:error,
+ "no more than one pickup event in the same process, referring to the same resource is allowed"}
true ->
{:ok, nil}
end
end)
+ |> Multi.put(key, evt)
end
-
-defp handle_multi("dropoff", evt, _) do
+defp handle_insert(key, %{action_id: "dropoff"} = evt, _) do
Multi.new()
- |> Multi.run(:checks, fn repo, _ ->
+ |> Multi.run("#{key}.checks", fn repo, _ ->
pair_evt =
from(e in EconomicEvent,
where: e.resource_inventoried_as_id == ^evt.resource_inventoried_as_id
@@ -340,14 +371,13 @@ defp handle_multi("dropoff", evt, _) do
]a))
|> repo.one!()
- single_ref? = fn ->
+ not_single_ref? = fn ->
where(EconomicEvent, [e],
e.resource_inventoried_as_id == ^evt.resource_inventoried_as_id
and e.id != ^evt.id
and e.action_id == "dropoff"
and e.output_of_id == ^evt.output_of_id)
|> repo.exists?()
- |> Kernel.not()
end
container? = fn ->
@@ -358,35 +388,32 @@ defp handle_multi("dropoff", evt, _) do
cond do
evt.provider_id != pair_evt.provider_id ->
{:error, "you don't have custody over this resource"}
-
evt.resource_quantity_has_unit_id != pair_evt.resource_quantity_has_unit_id ->
{:error, "the unit of resource quantity must match with the unit of the paired event"}
-
container?.() && evt.resource_quantity_has_numerical_value != pair_evt.resource_quantity_has_numerical_value ->
{:error, "the dropoff events need to fully dropoff the resource"}
-
- not single_ref?.() ->
- {:error, "no more than one dropoff event in the same process, referring to the same resource is allowed"}
-
+ not_single_ref?.() ->
+ {:error,
+ "no more than one dropoff event in the same process, referring to the same resource is allowed"}
true ->
{:ok, nil}
end
end)
- |> Multi.run(:set, fn repo, _ ->
+ |> Multi.run("#{key}.set", fn repo, _ ->
if evt.to_location_id do
q = where(EconomicResource, [r],
- r.id == ^evt.resource_inventoried_as_id
- or r.contained_in_id == ^evt.resource_inventoried_as_id)
+ r.id == ^evt.resource_inventoried_as_id
+ or r.contained_in_id == ^evt.resource_inventoried_as_id)
{:ok, repo.update_all(q, set: [current_location_id: evt.to_location_id])}
else
{:ok, nil}
end
end)
+ |> Multi.put(key, evt)
end
-
-defp handle_multi("accept", evt, _) do
+defp handle_insert(key, %{action_id: "accept"} = evt, _) do
Multi.new()
- |> Multi.run(:checks, fn repo, _ ->
+ |> Multi.run("#{key}.checks", fn repo, _ ->
fields = ~w[
custodian_id
onhand_quantity_has_numerical_value onhand_quantity_has_unit_id
@@ -403,14 +430,13 @@ defp handle_multi("accept", evt, _) do
where(EconomicResource, contained_in_id: ^evt.resource_inventoried_as_id)
|> repo.exists?())
- single_ref? = fn ->
+ not_single_ref? = fn ->
where(EconomicEvent, [e],
e.resource_inventoried_as_id == ^evt.resource_inventoried_as_id
and e.id != ^evt.id
and e.action_id == "accept"
and e.input_of_id == ^evt.input_of_id)
|> repo.exists?()
- |> Kernel.not()
end
any_combine_separate? = fn ->
@@ -440,21 +466,21 @@ defp handle_multi("accept", evt, _) do
any_combine_separate?.() ->
{:error, "you can't add another accept event to the same process where there are at least one combine or separate events"}
- not single_ref?.() ->
+ not_single_ref?.() ->
{:error, "no more than one accept event in the same process, referring to the same resource is allowed"}
true ->
{:ok, nil}
end
end)
- |> Multi.update_all(:dec, where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
+ |> Multi.update_all("#{key}.dec", where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
onhand_quantity_has_numerical_value: -evt.resource_quantity_has_numerical_value,
])
+ |> Multi.put(key, evt)
end
-
-defp handle_multi("modify", evt, _) do
+defp handle_insert(key, %{action_id: "modify"} = evt, _) do
Multi.new()
- |> Multi.run(:checks, fn repo, _ ->
+ |> Multi.run("#{key}.checks", fn repo, _ ->
pair_evt =
from(e in EconomicEvent,
where: e.resource_inventoried_as_id == ^evt.resource_inventoried_as_id
@@ -466,48 +492,43 @@ defp handle_multi("modify", evt, _) do
]a))
|> repo.one!()
- single_ref? = fn ->
+ not_single_ref? = fn ->
where(EconomicEvent, [e],
e.resource_inventoried_as_id == ^evt.resource_inventoried_as_id
and e.id != ^evt.id
and e.action_id == "modify"
and e.output_of_id == ^evt.output_of_id)
|> repo.exists?()
- |> Kernel.not()
end
cond do
evt.provider_id != pair_evt.provider_id ->
{:error, "you don't have custody over this resource"}
-
evt.resource_quantity_has_unit_id != pair_evt.resource_quantity_has_unit_id ->
{:error, "the unit of resource quantity must match with the unit of the paired event"}
-
evt.resource_quantity_has_numerical_value != pair_evt.resource_quantity_has_numerical_value ->
{:error, "the modify events need to fully modify the resource"}
-
- not single_ref?.() ->
+ not_single_ref?.() ->
{:error, "no more than one modify event in the same process, referring to the same resource is allowed"}
-
true ->
{:ok, nil}
end
end)
- |> Multi.update_all(:inc, where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
+ |> Multi.update_all("#{key}.inc", where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
onhand_quantity_has_numerical_value: evt.resource_quantity_has_numerical_value,
])
- |> Multi.update_all(:stage, fn _ ->
+ |> Multi.update_all("#{key}.stage", fn _ ->
from(r in EconomicResource,
join: e in EconomicEvent, on: e.id == ^evt.id,
join: p in assoc(e, :output_of),
where: r.id == e.resource_inventoried_as_id,
update: [set: [stage_id: p.based_on_id]])
end, [])
+ |> Multi.put(key, evt)
end
-
-defp handle_multi("transferCustody", evt, res_params) do
+defp handle_insert(key, %{action_id: "transferCustody"} = evt, res_params) do
Multi.new()
- |> Multi.run(:checks, fn repo, _ ->
+ |> Multi.run("#{key}.checks", fn repo, _ ->
%{resource_inventoried_as_id: res_id, to_resource_inventoried_as_id: to_res_id} = evt
res_ids =
if to_res_id do
@@ -543,51 +564,43 @@ defp handle_multi("transferCustody", evt, res_params) do
cond do
evt.provider_id != res.custodian_id ->
{:error, "you don't have custody over this resource"}
-
res.contained? ->
{:error, "you can't transfer-custody a contained resource"}
-
evt.resource_quantity_has_unit_id != res.onhand_quantity_has_unit_id ->
{:error, "the unit of resource-quantity must match with the unit of resource-inventoried-as"}
-
res.container? and res.onhand_quantity_has_numerical_value <= 0 ->
{:error, "the transfer-custody events need container resources to have positive onhand-quantity"}
-
res.container? && evt.resource_quantity_has_numerical_value != res.onhand_quantity_has_numerical_value ->
{:error, "the transfer-custody events need to fully transfer the resource"}
-
res.container? && to_res ->
{:error, "you can't transfer-custody a container resource into another resource"}
-
to_res && to_res.contained? ->
{:error, "you can't transfer-custody into a contained resource"}
-
to_res && to_res.container? ->
{:error, "you can't transfer-custody into a container resource"}
-
to_res && evt.resource_quantity_has_unit_id != to_res.onhand_quantity_has_unit_id ->
{:error, "the unit of resource-quantity must match with the unit of to-resource-inventoried-as"}
-
to_res && res.conforms_to_id != to_res.conforms_to_id ->
{:error, "the resources must conform to the same specification"}
-
true ->
# some fields of the resource is required for the following multis
{:ok, res}
end
end)
- |> Multi.merge(fn %{checks: res} ->
+ |> Multi.merge(fn changes ->
+ res = Map.fetch!(changes, "#{key}.checks")
if evt.to_resource_inventoried_as_id do
Multi.new()
- |> Multi.update_all(:dec, where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
+ |> Multi.update_all("#{key}.dec", where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
onhand_quantity_has_numerical_value: -evt.resource_quantity_has_numerical_value,
])
- |> Multi.update_all(:inc, where(EconomicResource, id: ^evt.to_resource_inventoried_as_id), inc: [
+ |> Multi.update_all("#{key}.inc", where(EconomicResource, id: ^evt.to_resource_inventoried_as_id), inc: [
onhand_quantity_has_numerical_value: evt.resource_quantity_has_numerical_value,
])
+ |> Multi.put(key, evt)
else
res_params =
- res_params
+ (res_params || %{})
|> Map.put(:primary_accountable_id, evt.receiver_id)
|> Map.put(:custodian_id, evt.receiver_id)
|> Map.put(:conforms_to_id, res.conforms_to_id)
@@ -608,19 +621,19 @@ defp handle_multi("transferCustody", evt, res_params) do
|> Map.put_new(:metadata, res.metadata)
Multi.new()
- |> Multi.insert(:to_eco_res, EconomicResource.chgset(res_params))
- |> Multi.update(:updated_evt, fn %{to_eco_res: res} ->
- Changeset.change(evt, to_resource_inventoried_as_id: res.id)
- end)
- |> Multi.update_all(:dec, where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
+ |> EconomicResource.Domain.multi_insert("#{key}.to_eco_res", res_params)
+ |> Multi.update(key, &Changeset.change(evt,
+ to_resource_inventoried_as_id: &1 |> Map.fetch!("#{key}.to_eco_res") |> Map.fetch!(:id)))
+ |> Multi.update_all("#{key}.dec", where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
onhand_quantity_has_numerical_value: -evt.resource_quantity_has_numerical_value,
])
- |> Multi.merge(fn %{updated_evt: evt} ->
+ |> Multi.merge(fn %{^key => evt} ->
if res.container? do
- Multi.update_all(Multi.new(), :set, where(EconomicResource, contained_in_id: ^evt.resource_inventoried_as_id), set: [
- contained_in_id: evt.to_resource_inventoried_as_id,
- custodian_id: evt.receiver_id,
- ])
+ Multi.update_all(Multi.new(), "#{key}.set",
+ where(EconomicResource, contained_in_id: ^evt.resource_inventoried_as_id), set: [
+ contained_in_id: evt.to_resource_inventoried_as_id,
+ custodian_id: evt.receiver_id,
+ ])
else
Multi.new()
end
@@ -628,10 +641,9 @@ defp handle_multi("transferCustody", evt, res_params) do
end
end)
end
-
-defp handle_multi("transferAllRights", evt, res_params) do
+defp handle_insert(key, %{action_id: "transferAllRights"} = evt, res_params) do
Multi.new()
- |> Multi.run(:checks, fn repo, _ ->
+ |> Multi.run("#{key}.checks", fn repo, _ ->
%{resource_inventoried_as_id: res_id, to_resource_inventoried_as_id: to_res_id} = evt
res_ids =
if to_res_id do
@@ -667,40 +679,31 @@ defp handle_multi("transferAllRights", evt, res_params) do
cond do
evt.provider_id != res.primary_accountable_id ->
{:error, "you don't have accountability over this resource"}
-
res.contained? ->
{:error, "you can't transfer-all-rights a contained resource"}
-
evt.resource_quantity_has_unit_id != res.accounting_quantity_has_unit_id ->
{:error, "the unit of resource-quantity must match with the unit of resource-inventoried-as"}
-
res.container? and res.accounting_quantity_has_numerical_value <= 0 ->
{:error, "the transfer-all-rights events need container resources to have positive accounting-quantity"}
-
res.container? && evt.resource_quantity_has_numerical_value != res.accounting_quantity_has_numerical_value ->
{:error, "the transfer-all-rights events need to fully transfer the resource"}
-
res.container? && to_res ->
{:error, "you can't transfer-all-rights a container resource into another resource"}
-
to_res && to_res.contained? ->
{:error, "you can't transfer-all-rights into a contained resource"}
-
to_res && to_res.container? ->
{:error, "you can't transfer-all-rights into a container resource"}
-
to_res && evt.resource_quantity_has_unit_id != to_res.accounting_quantity_has_unit_id ->
{:error, "the unit of resource-quantity must match with the unit of to-resource-inventoried-as"}
-
to_res && res.conforms_to_id != to_res.conforms_to_id ->
{:error, "the resources must conform to the same specification"}
-
true ->
# some fields of the resource is required for the following multis
{:ok, res}
end
end)
- |> Multi.merge(fn %{checks: res} ->
+ |> Multi.merge(fn changes ->
+ res = Map.fetch!(changes, "#{key}.checks")
if evt.to_resource_inventoried_as_id do
Multi.new()
|> Multi.update_all(:dec, where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
@@ -709,9 +712,10 @@ defp handle_multi("transferAllRights", evt, res_params) do
|> Multi.update_all(:inc, where(EconomicResource, id: ^evt.to_resource_inventoried_as_id), inc: [
accounting_quantity_has_numerical_value: evt.resource_quantity_has_numerical_value,
])
+ |> Multi.put(key, evt)
else
res_params =
- res_params
+ (res_params || %{})
|> Map.put(:primary_accountable_id, evt.receiver_id)
|> Map.put(:custodian_id, evt.receiver_id)
|> Map.put(:conforms_to_id, res.conforms_to_id)
@@ -731,16 +735,15 @@ defp handle_multi("transferAllRights", evt, res_params) do
|> Map.put_new(:metadata, res.metadata)
Multi.new()
- |> Multi.insert(:to_eco_res, EconomicResource.chgset(res_params))
- |> Multi.update(:updated_evt, fn %{to_eco_res: res} ->
- Changeset.change(evt, to_resource_inventoried_as_id: res.id)
- end)
- |> Multi.update_all(:dec, where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
+ |> EconomicResource.Domain.multi_insert("#{key}.to_eco_res", res_params)
+ |> Multi.update(key, &Changeset.change(evt,
+ to_resource_inventoried_as_id: &1 |> Map.fetch!("#{key}.to_eco_res") |> Map.fetch!(:id)))
+ |> Multi.update_all("#{key}.dec", where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
accounting_quantity_has_numerical_value: -evt.resource_quantity_has_numerical_value,
])
- |> Multi.merge(fn %{updated_evt: evt} ->
+ |> Multi.merge(fn %{^key => evt} ->
if res.container? do
- Multi.update_all(Multi.new(), :set, where(EconomicResource, contained_in_id: ^evt.resource_inventoried_as_id), set: [
+ Multi.update_all(Multi.new(), "#{key}.set", where(EconomicResource, contained_in_id: ^evt.resource_inventoried_as_id), set: [
contained_in_id: evt.to_resource_inventoried_as_id,
primary_accountable_id: evt.receiver_id,
])
@@ -751,10 +754,9 @@ defp handle_multi("transferAllRights", evt, res_params) do
end
end)
end
-
-defp handle_multi("transfer", evt, res_params) do
+defp handle_insert(key, %{action_id: "transfer"} = evt, res_params) do
Multi.new()
- |> Multi.run(:checks, fn repo, _ ->
+ |> Multi.run("#{key}.checks", fn repo, _ ->
%{resource_inventoried_as_id: res_id, to_resource_inventoried_as_id: to_res_id} = evt
res_ids =
if to_res_id do
@@ -791,62 +793,51 @@ defp handle_multi("transfer", evt, res_params) do
cond do
evt.provider_id != res.primary_accountable_id ->
{:error, "you don't have accountability over this resource"}
-
evt.provider_id != res.custodian_id ->
{:error, "you don't have custody over this resource"}
-
res.contained? ->
{:error, "you can't transfer a contained resource"}
-
evt.resource_quantity_has_unit_id != res.accounting_quantity_has_unit_id ->
{:error, "the unit of resource-quantity must match with the unit of resource-inventoried-as"}
-
res.container? and res.accounting_quantity_has_numerical_value <= 0 ->
{:error, "the transfer events need container resources to have positive accounting-quantity"}
-
res.container? and res.onhand_quantity_has_numerical_value <= 0 ->
{:error, "the transfer events need container resources to have positive onhand-quantity"}
-
res.container? && evt.resource_quantity_has_numerical_value != res.accounting_quantity_has_numerical_value ->
{:error, "the transfer events need to fully transfer the resource"}
-
res.container? && evt.resource_quantity_has_numerical_value != res.onhand_quantity_has_numerical_value ->
{:error, "the transfer events need to fully transfer the resource"}
-
res.container? && to_res ->
{:error, "you can't transfer a container resource into another resource"}
-
to_res && to_res.contained? ->
{:error, "you can't transfer into a contained resource"}
-
to_res && to_res.container? ->
{:error, "you can't transfer into a container resource"}
-
to_res && evt.resource_quantity_has_unit_id != to_res.accounting_quantity_has_unit_id ->
{:error, "the unit of resource-quantity must match with the unit of to-resource-inventoried-as"}
-
to_res && res.conforms_to_id != to_res.conforms_to_id ->
{:error, "the resources must conform to the same specification"}
-
true ->
# some fields of the resource is required for the following multis
{:ok, res}
end
end)
- |> Multi.merge(fn %{checks: res} ->
+ |> Multi.merge(fn changes ->
+ res = Map.fetch!(changes, "#{key}.checks")
if evt.to_resource_inventoried_as_id do
Multi.new()
- |> Multi.update_all(:dec, where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
+ |> Multi.update_all("#{key}.dec", where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
accounting_quantity_has_numerical_value: -evt.resource_quantity_has_numerical_value,
onhand_quantity_has_numerical_value: -evt.resource_quantity_has_numerical_value,
])
- |> Multi.update_all(:inc, where(EconomicResource, id: ^evt.to_resource_inventoried_as_id), inc: [
+ |> Multi.update_all("#{key}.inc", where(EconomicResource, id: ^evt.to_resource_inventoried_as_id), inc: [
accounting_quantity_has_numerical_value: evt.resource_quantity_has_numerical_value,
onhand_quantity_has_numerical_value: evt.resource_quantity_has_numerical_value,
])
+ |> Multi.put(key, evt)
else
res_params =
- res_params
+ (res_params || %{})
|> Map.put(:primary_accountable_id, evt.receiver_id)
|> Map.put(:custodian_id, evt.receiver_id)
|> Map.put(:conforms_to_id, res.conforms_to_id)
@@ -867,17 +858,16 @@ defp handle_multi("transfer", evt, res_params) do
|> Map.put_new(:metadata, res.metadata)
Multi.new()
- |> Multi.insert(:to_eco_res, EconomicResource.chgset(res_params))
- |> Multi.update(:updated_evt, fn %{to_eco_res: res} ->
- Changeset.change(evt, to_resource_inventoried_as_id: res.id)
- end)
- |> Multi.update_all(:dec, where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
+ |> EconomicResource.Domain.multi_insert("#{key}.to_eco_res", res_params)
+ |> Multi.update(key, &Changeset.change(evt,
+ to_resource_inventoried_as_id: &1 |> Map.fetch!("#{key}.to_eco_res") |> Map.fetch!(:id)))
+ |> Multi.update_all("#{key}.dec", where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
accounting_quantity_has_numerical_value: -evt.resource_quantity_has_numerical_value,
onhand_quantity_has_numerical_value: -evt.resource_quantity_has_numerical_value,
])
- |> Multi.merge(fn %{updated_evt: evt} ->
+ |> Multi.merge(fn %{^key => evt} ->
if res.container? do
- Multi.update_all(Multi.new(), :set, where(EconomicResource, contained_in_id: ^evt.resource_inventoried_as_id), set: [
+ Multi.update_all(Multi.new(), "#{key}.set", where(EconomicResource, contained_in_id: ^evt.resource_inventoried_as_id), set: [
contained_in_id: evt.to_resource_inventoried_as_id,
primary_accountable_id: evt.receiver_id,
custodian_id: evt.receiver_id,
@@ -889,10 +879,9 @@ defp handle_multi("transfer", evt, res_params) do
end
end)
end
-
-defp handle_multi("move", evt, res_params) do
+defp handle_insert(key, %{action_id: "move"} = evt, res_params) do
Multi.new()
- |> Multi.run(:checks, fn repo, _ ->
+ |> Multi.run("#{key}.checks", fn repo, _ ->
%{resource_inventoried_as_id: res_id, to_resource_inventoried_as_id: to_res_id} = evt
res_ids =
if to_res_id do
@@ -929,68 +918,55 @@ defp handle_multi("move", evt, res_params) do
cond do
evt.provider_id != res.primary_accountable_id ->
{:error, "you don't have accountability over resource-inventoried-as"}
-
evt.provider_id != res.custodian_id ->
{:error, "you don't have custody over resource-inventoried-as"}
-
res.contained? ->
{:error, "you can't move a contained resource"}
-
evt.resource_quantity_has_unit_id != res.accounting_quantity_has_unit_id ->
{:error, "the unit of resource-quantity must match with the unit of resource-inventoried-as"}
-
res.container? and res.accounting_quantity_has_numerical_value <= 0 ->
{:error, "the move events need container resources to have positive accounting-quantity"}
-
res.container? and res.onhand_quantity_has_numerical_value <= 0 ->
{:error, "the move events need container resources to have positive onhand-quantity"}
-
res.container? && evt.resource_quantity_has_numerical_value != res.accounting_quantity_has_numerical_value ->
{:error, "the move events need to fully move the resource"}
-
res.container? && evt.resource_quantity_has_numerical_value != res.onhand_quantity_has_numerical_value ->
{:error, "the move events need to fully move the resource"}
-
res.container? && to_res ->
{:error, "you can't move a container resource into another resource"}
-
to_res && evt.provider_id != to_res.primary_accountable_id ->
{:error, "you don't have accountability over to-resource-inventoried-as"}
-
to_res && evt.provider_id != to_res.custodian_id ->
{:error, "you don't have custody over to-resource-inventoried-as"}
-
to_res && to_res.contained? ->
{:error, "you can't move into a contained resource"}
-
to_res && to_res.container? ->
{:error, "you can't move into a container resource"}
-
to_res && evt.resource_quantity_has_unit_id != to_res.accounting_quantity_has_unit_id ->
{:error, "the unit of resource-quantity must match with the unit of to-resource-inventoried-as"}
-
to_res && res.conforms_to_id != to_res.conforms_to_id ->
{:error, "the resources must conform to the same specification"}
-
true ->
# some fields of the resource is required for the following multis
{:ok, res}
end
end)
- |> Multi.merge(fn %{checks: res} ->
+ |> Multi.merge(fn changes ->
+ res = Map.fetch!(changes, "#{key}.checks")
if evt.to_resource_inventoried_as_id do
Multi.new()
- |> Multi.update_all(:dec, where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
+ |> Multi.update_all("#{key}.dec", where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
accounting_quantity_has_numerical_value: -evt.resource_quantity_has_numerical_value,
onhand_quantity_has_numerical_value: -evt.resource_quantity_has_numerical_value,
])
- |> Multi.update_all(:inc, where(EconomicResource, id: ^evt.to_resource_inventoried_as_id), inc: [
+ |> Multi.update_all("#{key}.inc", where(EconomicResource, id: ^evt.to_resource_inventoried_as_id), inc: [
accounting_quantity_has_numerical_value: evt.resource_quantity_has_numerical_value,
onhand_quantity_has_numerical_value: evt.resource_quantity_has_numerical_value,
])
+ |> Multi.put(key, evt)
else
res_params =
- res_params
+ (res_params || %{})
|> Map.put(:primary_accountable_id, evt.receiver_id)
|> Map.put(:custodian_id, evt.receiver_id)
|> Map.put(:conforms_to_id, res.conforms_to_id)
@@ -1011,17 +987,16 @@ defp handle_multi("move", evt, res_params) do
|> Map.put_new(:metadata, res.metadata)
Multi.new()
- |> Multi.insert(:to_eco_res, EconomicResource.chgset(res_params))
- |> Multi.update(:updated_evt, fn %{to_eco_res: res} ->
- Changeset.change(evt, to_resource_inventoried_as_id: res.id)
- end)
+ |> EconomicResource.Domain.multi_insert("#{key}.to_eco_res", res_params)
+ |> Multi.update(key, &Changeset.change(evt,
+ to_resource_inventoried_as_id: &1 |> Map.fetch!("#{key}.to_eco_res") |> Map.fetch!(:id)))
|> Multi.update_all(:dec, where(EconomicResource, id: ^evt.resource_inventoried_as_id), inc: [
accounting_quantity_has_numerical_value: -evt.resource_quantity_has_numerical_value,
onhand_quantity_has_numerical_value: -evt.resource_quantity_has_numerical_value,
])
- |> Multi.merge(fn %{updated_evt: evt} ->
+ |> Multi.merge(fn %{^key => evt} ->
if res.container? do
- Multi.update_all(Multi.new(), :set, where(EconomicResource, contained_in_id: ^evt.resource_inventoried_as_id), set: [
+ Multi.update_all(Multi.new(), "#{key}.set", where(EconomicResource, contained_in_id: ^evt.resource_inventoried_as_id), set: [
contained_in_id: evt.to_resource_inventoried_as_id,
primary_accountable_id: evt.receiver_id,
custodian_id: evt.receiver_id,
@@ -1034,84 +1009,18 @@ defp handle_multi("move", evt, res_params) do
end)
end
-@spec update(id(), params())
- :: {:ok, EconomicEvent.t()} | {:error, String.t() | chgset()}
-def update(id, params) do
- Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &EconomicEvent.chgset(&1.one, params))
- |> Repo.transaction()
- |> case do
- {:ok, %{update: ee}} -> {:ok, ee}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
- end
-end
-
-@spec preload(EconomicEvent.t(), :action | :input_of | :output_of
- | :provider | :receiver
- | :resource_inventoried_as | :to_resource_inventoried_as
- | :resource_conforms_to | :resource_quantity | :effort_quantity
- | :to_location | :at_location | :realization_of | :triggered_by
- | :previous_event)
- :: EconomicEvent.t()
-def preload(eco_evt, :action) do
- Action.preload(eco_evt, :action)
-end
-
-def preload(eco_evt, :input_of) do
- Repo.preload(eco_evt, :input_of)
-end
-
-def preload(eco_evt, :output_of) do
- Repo.preload(eco_evt, :output_of)
-end
-
-def preload(eco_evt, :provider) do
- Repo.preload(eco_evt, :provider)
-end
-
-def preload(eco_evt, :receiver) do
- Repo.preload(eco_evt, :receiver)
-end
-
-def preload(eco_evt, :resource_inventoried_as) do
- Repo.preload(eco_evt, :resource_inventoried_as)
-end
-
-def preload(eco_evt, :to_resource_inventoried_as) do
- Repo.preload(eco_evt, :to_resource_inventoried_as)
-end
-
-def preload(eco_evt, :resource_conforms_to) do
- Repo.preload(eco_evt, :resource_conforms_to)
-end
-
-def preload(eco_evt, :resource_quantity) do
- Measure.preload(eco_evt, :resource_quantity)
-end
-
-def preload(eco_evt, :effort_quantity) do
- Measure.preload(eco_evt, :effort_quantity)
-end
-
-def preload(eco_evt, :to_location) do
- Repo.preload(eco_evt, :to_location)
-end
-
-def preload(eco_evt, :at_location) do
- Repo.preload(eco_evt, :at_location)
-end
-
-def preload(eco_evt, :realization_of) do
- Repo.preload(eco_evt, :realization_of)
-end
-
-def preload(eco_evt, :triggered_by) do
- Repo.preload(eco_evt, :triggered_by)
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &EconomicEvent.changeset(Map.fetch!(&1, "#{key}.one"), params))
end
-def preload(eco_evt, :previous_event) do
- Repo.preload(eco_evt, :previous_event)
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
end
end
diff --git a/src/zenflows/vf/economic_event/resolv.ex b/src/zenflows/vf/economic_event/resolv.ex
@@ -16,10 +16,11 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.EconomicEvent.Resolv do
-@moduledoc "Resolvers of EconomicEvent."
+@moduledoc false
use Absinthe.Schema.Notation
+alias Zenflows.GQL.Connection
alias Zenflows.VF.EconomicEvent.Domain
def economic_event(params, _) do
@@ -27,26 +28,16 @@ def economic_event(params, _) do
end
def economic_events(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_economic_event(%{event: evt_params} = params, _) do
res_params = params[:new_inventoried_resource]
-
- case Domain.create(evt_params, res_params) do
- {:ok, evt, res, nil} ->
- evt = Map.put(evt, :resource_inventoried_as, res) # tiny optimization
- {:ok, %{economic_event: evt}}
-
- {:ok, evt, nil, to_res} ->
- evt = Map.put(evt, :to_resource_inventoried_as, to_res) # tiny optimization
- {:ok, %{economic_event: evt}}
-
- {:ok, evt} ->
- {:ok, %{economic_event: evt}}
-
- {:error, err} ->
- {:error, err}
+ with {:ok, evt} <- Domain.create(evt_params, res_params) do
+ {:ok, %{economic_event: evt}}
end
end
diff --git a/src/zenflows/vf/economic_event/type.ex b/src/zenflows/vf/economic_event/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.EconomicEvent.Type do
-@moduledoc "GraphQL types of EconomicEvents."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/economic_resource.ex b/src/zenflows/vf/economic_resource.ex
@@ -20,6 +20,8 @@ defmodule Zenflows.VF.EconomicResource do
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.File
alias Zenflows.VF.{
Action,
@@ -31,7 +33,6 @@ alias Zenflows.VF.{
ResourceSpecification,
SpatialThing,
Unit,
- Validate,
}
@type t() :: %__MODULE__{
@@ -111,8 +112,8 @@ end
]a
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
@@ -124,8 +125,8 @@ def chgset(schema \\ %__MODULE__{}, params) do
|> Validate.name(:version)
|> Validate.name(:licensor)
|> Validate.name(:license)
- |> require_quantity_units_same()
- |> Changeset.cast_assoc(:images, with: &File.chgset/2)
+ |> Validate.value_eq([:accounting_quantity_has_unit_id, :onhand_quantity_has_unit_id])
+ |> Changeset.cast_assoc(:images)
|> Changeset.assoc_constraint(:conforms_to)
|> Changeset.assoc_constraint(:accounting_quantity_has_unit)
|> Changeset.assoc_constraint(:onhand_quantity_has_unit)
@@ -137,21 +138,4 @@ def chgset(schema \\ %__MODULE__{}, params) do
|> Changeset.assoc_constraint(:contained_in)
|> Changeset.assoc_constraint(:unit_of_effort)
end
-
-# Require that `:accounting_quantity_has_unit_id` and
-# `:onhand_quantity_has_unit_id` be the same.
-@spec require_quantity_units_same(Changeset.t()) :: Changeset.t()
-def require_quantity_units_same(cset) do
- accnt_unit = Changeset.get_field(cset, :accounting_quantity_has_unit_id)
- onhnd_unit = Changeset.get_field(cset, :onhand_quantity_has_unit_id)
-
- if accnt_unit != onhnd_unit do
- msg = "has_unit: quantity units must be same"
- cset
- |> Changeset.add_error(:accounting_quantity, msg)
- |> Changeset.add_error(:onhand_quantity, msg)
- else
- cset
- end
-end
end
diff --git a/src/zenflows/vf/economic_resource/domain.ex b/src/zenflows/vf/economic_resource/domain.ex
@@ -18,8 +18,8 @@
defmodule Zenflows.VF.EconomicResource.Domain do
@moduledoc "Domain logic of EconomicResources."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.{
Action,
EconomicResource,
@@ -27,12 +27,7 @@ alias Zenflows.VF.{
Measure,
}
-@typep repo() :: Ecto.Repo.t()
-@typep error() :: Ecto.Changeset.t() | String.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, EconomicResource.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -43,13 +38,26 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Filter.error() | Paging.result()
-def all(params \\ %{}) do
- with {:ok, q} <- Filter.filter(params[:filter] || %{}) do
- Paging.page(q, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
+ :: EconomicResource.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [EconomicResource.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ with {:ok, q} <- Filter.all(page) do
+ {:ok, Page.all(q, page)}
end
end
+@spec all!(Page.t()) :: [EconomicResource.t()]
+def all!(page \\ Page.new()) do
+ {:ok, q} = Filter.all(page)
+ Page.all(q, page)
+end
+
@spec classifications() :: [String.t()]
def classifications() do
import Ecto.Query
@@ -59,83 +67,85 @@ def classifications() do
|> Repo.all()
end
-@spec update(id(), params()) :: {:ok, EconomicResource.t()} | {:error, error()}
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, EconomicResource.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &EconomicResource.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: er}} -> {:ok, er}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id()) :: {:ok, EconomicResource.t()} | {:error, error()}
+@spec update!(Schema.id(), Schema.params()) :: EconomicResource.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id()) ::
+ {:ok, EconomicResource.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, & &1.one)
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: er}} -> {:ok, er}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+@spec delete!(Schema.id) :: EconomicResource.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
@spec preload(EconomicResource.t(), :images
| :conforms_to | :accounting_quantity
| :onhand_quantity | :primary_accountable | :custodian
| :stage | :state | :current_location | :lot | :contained_in
| :unit_of_effort)
:: EconomicResource.t()
-def preload(eco_res, :images) do
- Repo.preload(eco_res, :images)
-end
-
-def preload(eco_res, :conforms_to) do
- Repo.preload(eco_res, :conforms_to)
-end
-
-def preload(eco_res, :accounting_quantity) do
- Measure.preload(eco_res, :accounting_quantity)
-end
-
-def preload(eco_res, :onhand_quantity) do
- Measure.preload(eco_res, :onhand_quantity)
-end
-
-def preload(eco_res, :primary_accountable) do
- Repo.preload(eco_res, :primary_accountable)
-end
-
-def preload(eco_res, :custodian) do
- Repo.preload(eco_res, :custodian)
-end
-
-def preload(eco_res, :stage) do
- Repo.preload(eco_res, :stage)
-end
-
-def preload(eco_res, :state) do
- Action.preload(eco_res, :state)
-end
-
-def preload(eco_res, :current_location) do
- Repo.preload(eco_res, :current_location)
-end
-
-def preload(eco_res, :lot) do
- Repo.preload(eco_res, :lot)
-end
-
-def preload(eco_res, :contained_in) do
- Repo.preload(eco_res, :contained_in)
-end
-
-def preload(eco_res, :unit_of_effort) do
- Repo.preload(eco_res, :unit_of_effort)
+def preload(eco_res, x) when x in ~w[
+ images conforms_to primary_accountable custodian lot
+ stage current_location contained_in unit_of_effort
+]a,
+ do: Repo.preload(eco_res, x)
+def preload(eco_res, x) when x in ~w[accounting_quantity onhand_quantity]a,
+ do: Measure.preload(eco_res, x)
+def preload(eco_res, :state),
+ do: Action.preload(eco_res, :state)
+
+@spec multi_key() :: atom()
+def multi_key(), do: :economic_resource
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, EconomicResource.changeset(params))
+end
+
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &EconomicResource.changeset(Map.fetch!(&1, "#{key}.one"), params))
+end
+
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
end
end
diff --git a/src/zenflows/vf/economic_resource/filter.ex b/src/zenflows/vf/economic_resource/filter.ex
@@ -16,62 +16,54 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.EconomicResource.Filter do
-@moduledoc "Filtering logic of EconomicResources."
-
-use Zenflows.DB.Schema
+@moduledoc false
import Ecto.Query
-alias Ecto.Query
-alias Zenflows.DB.{Filter, ID}
-alias Zenflows.VF.{EconomicResource, Validate}
-
-@type error() :: Filter.error()
+alias Ecto.{Changeset, Queryable}
+alias Zenflows.DB.{ID, Page, Schema, Validate}
+alias Zenflows.VF.EconomicResource
-@spec filter(Filter.params()) :: Filter.result()
-def filter(params) do
- case chgset(params) do
- %{valid?: true, changes: c} ->
- {:ok, Enum.reduce(c, EconomicResource, &f(&2, &1))}
- %{valid?: false} = cset ->
- {:error, cset}
+@spec all(Page.t()) :: {:ok, Queryable.t()} | {:error, Changeset.t()}
+def all(%{filter: nil}), do: {:ok, EconomicResource}
+def all(%{filter: params}) do
+ with {:ok, filters} <- all_validate(params) do
+ Enum.reduce(filters, EconomicResource, &all_f(&2, &1))
end
end
-@spec f(Query.t(), {atom(), term()}) :: Query.t()
-defp f(q, {:classified_as, v}),
+@spec all_f(Queryable.t(), {atom(), term()}) :: Queryable.t()
+defp all_f(q, {:classified_as, v}),
do: where(q, [x], fragment("? @> ?", x.classified_as, ^v))
-defp f(q, {:primary_accountable, v}),
+defp all_f(q, {:primary_accountable, v}),
do: where(q, [x], x.primary_accountable_id in ^v)
-defp f(q, {:custodian, v}),
+defp all_f(q, {:custodian, v}),
do: where(q, [x], x.custodian_id in ^v)
-defp f(q, {:conforms_to, v}),
+defp all_f(q, {:conforms_to, v}),
do: where(q, [x], x.conforms_to_id in ^v)
-defp f(q, {:gt_onhand_quantity_has_numerical_value, v}),
+defp all_f(q, {:gt_onhand_quantity_has_numerical_value, v}),
do: where(q, [x], x.onhand_quantity_has_numerical_value > ^v)
-embedded_schema do
- field :classified_as, {:array, :string}
- field :primary_accountable, {:array, ID}
- field :custodian, {:array, ID}
- field :conforms_to, {:array, ID}
- field :gt_onhand_quantity_has_numerical_value, :float
-end
-
-@cast ~w[
- classified_as primary_accountable custodian conforms_to
- gt_onhand_quantity_has_numerical_value
-]a
-
-@spec chgset(params()) :: Changeset.t()
-defp chgset(params) do
- %__MODULE__{}
- |> Changeset.cast(params, @cast)
+@spec all_validate(Schema.params()) ::
+ {:ok, Changeset.data()} | {:error, Changeset.t()}
+defp all_validate(params) do
+ {%{}, %{
+ classified_as: {:array, :string},
+ primary_accountable: {:array, ID},
+ custodian: {:array, ID},
+ conforms_to: {:array, ID},
+ gt_onhand_quantity_has_numerical_value: :float,
+ }}
+ |> Changeset.cast(params, ~w[
+ classified_as primary_accountable custodian conforms_to
+ gt_onhand_quantity_has_numerical_value
+ ]a)
|> Validate.class(:classified_as)
|> Validate.class(:primary_accountable)
|> Validate.class(:custodian)
|> Validate.class(:conforms_to)
|> Changeset.validate_number(:gt_onhand_quantity_has_numerical_value,
greater_than_or_equal_to: 0)
+ |> Changeset.apply_action(nil)
end
end
diff --git a/src/zenflows/vf/economic_resource/resolv.ex b/src/zenflows/vf/economic_resource/resolv.ex
@@ -16,10 +16,11 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.EconomicResource.Resolv do
-@moduledoc "Resolvers of EconomicResource."
+@moduledoc false
use Absinthe.Schema.Notation
+alias Zenflows.GQL.Connection
alias Zenflows.VF.EconomicResource.Domain
def economic_resource(params, _) do
@@ -27,7 +28,10 @@ def economic_resource(params, _) do
end
def economic_resources(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def economic_resource_classifications(_, _) do
diff --git a/src/zenflows/vf/economic_resource/type.ex b/src/zenflows/vf/economic_resource/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.EconomicResource.Type do
-@moduledoc "GraphQL types of EconomicResources."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/event_or_commitment.ex b/src/zenflows/vf/event_or_commitment.ex
@@ -22,6 +22,8 @@ An EconomicEvent or Commitment, mutually exclusive.
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.VF.{Commitment, EconomicEvent}
@type t() :: %__MODULE__{
@@ -38,57 +40,12 @@ end
@cast ~w[event_id commitment_id]a
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
- |> mutex_check()
+ |> Validate.exist_xor([:event_id, :commitment_id], method: :both)
|> Changeset.assoc_constraint(:event)
|> Changeset.assoc_constraint(:commitment)
end
-
-# Validate mutual exclusivity of having either one of EconomicEvent
-# or Commitment.
-@spec mutex_check(Changeset.t()) :: Changeset.t()
-defp mutex_check(cset) do
- # credo:disable-for-previous-line Credo.Check.Refactor.CyclomaticComplexity
-
- {data_evt, chng_evt} =
- case Changeset.fetch_field(cset, :event_id) do
- {:data, x} -> {x, nil}
- {:changes, x} -> {nil, x}
- end
- {data_comm, chng_comm} =
- case Changeset.fetch_field(cset, :commitment_id) do
- {:data, x} -> {x, nil}
- {:changes, x} -> {nil, x}
- end
-
- cond do
- data_evt && chng_comm ->
- msg = "commitment is not allowed in this record"
- Changeset.add_error(cset, :commitment_id, msg)
-
- data_comm && chng_evt ->
- msg = "event is not allowed in this record"
- Changeset.add_error(cset, :event_id, msg)
-
- chng_evt && chng_comm ->
- msg = "economic events and commitments are mutually exclusive"
-
- cset
- |> Changeset.add_error(:event_id, msg)
- |> Changeset.add_error(:commitment_id, msg)
-
- chng_evt || chng_comm ->
- cset
-
- true ->
- msg = "economic events or commitments is required"
-
- cset
- |> Changeset.add_error(:event_id, msg)
- |> Changeset.add_error(:commitment_id, msg)
- end
-end
end
diff --git a/src/zenflows/vf/fulfillment.ex b/src/zenflows/vf/fulfillment.ex
@@ -23,12 +23,13 @@ events that fully or partially satisfy one or more commitments.
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.VF.{
Commitment,
EconomicEvent,
Measure,
Unit,
- Validate,
}
@type t() :: %__MODULE__{
@@ -56,8 +57,8 @@ end
@cast @reqr ++ ~w[resource_quantity effort_quantity note]a
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/intent.ex b/src/zenflows/vf/intent.ex
@@ -22,6 +22,8 @@ to economic events (sometimes through commitments).
"""
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.File
alias Zenflows.VF.{
Action,
@@ -33,7 +35,6 @@ alias Zenflows.VF.{
ResourceSpecification,
SpatialThing,
Unit,
- Validate,
}
@type t() :: %__MODULE__{
@@ -108,15 +109,15 @@ end
]a # in_scope_of_id
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
- |> mutex_check()
+ |> Validate.exist_xor([:provider_id, :receiver_id], method: :both)
|> Validate.name(:name)
|> Validate.note(:note)
- |> Changeset.cast_assoc(:images, with: &File.chgset/2)
+ |> Changeset.cast_assoc(:images)
|> Validate.class(:resource_classified_as)
|> Measure.cast(:resource_quantity)
|> Measure.cast(:effort_quantity)
@@ -129,48 +130,4 @@ def chgset(schema \\ %__MODULE__{}, params) do
|> Changeset.assoc_constraint(:resource_inventoried_as)
|> Changeset.assoc_constraint(:at_location)
end
-
-# Validate that provider and receiver are mutually exclusive.
-@spec mutex_check(Changeset.t()) :: Changeset.t()
-defp mutex_check(cset) do
- # credo:disable-for-previous-line Credo.Check.Refactor.CyclomaticComplexity
-
- {data_prov, chng_prov, field_prov} =
- case Changeset.fetch_field(cset, :provider_id) do
- {:data, x} -> {x, nil, x}
- {:changes, x} -> {nil, x, x}
- end
- {data_recv, chng_recv, field_recv} =
- case Changeset.fetch_field(cset, :receiver_id) do
- {:data, x} -> {x, nil, x}
- {:changes, x} -> {nil, x, x}
- end
-
- cond do
- data_prov && chng_recv ->
- msg = "receiver is not allowed in this record"
- Changeset.add_error(cset, :receiver_id, msg)
-
- data_recv && chng_prov ->
- msg = "provider is not allowed in this record"
- Changeset.add_error(cset, :provider_id, msg)
-
- chng_prov && chng_recv ->
- msg = "receiver is mutually exclusive with provider"
-
- cset
- |> Changeset.add_error(:provider_id, msg)
- |> Changeset.add_error(:receiver_id, msg)
-
- field_prov || field_recv ->
- cset
-
- true ->
- msg = "either provider or receiver is required"
-
- cset
- |> Changeset.add_error(:provider_id, msg)
- |> Changeset.add_error(:receiver_id, msg)
- end
-end
end
diff --git a/src/zenflows/vf/intent/domain.ex b/src/zenflows/vf/intent/domain.ex
@@ -18,20 +18,15 @@
defmodule Zenflows.VF.Intent.Domain do
@moduledoc "Domain logic of Intents."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.{
Action,
Intent,
Measure,
}
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, Intent.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -42,100 +37,120 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(Intent, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: Intent.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [Intent.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(Intent, page)}
+end
+
+@spec all!(Page.t()) :: [Intent.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
end
-@spec create(params()) :: {:ok, Intent.t()} | {:error, chgset()}
+@spec create(Schema.params()) :: {:ok, Intent.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, Intent.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: i}} -> {:ok, i}
+ {:ok, %{^key => value}} -> {:ok, value}
{:error, _, cset, _} -> {:error, cset}
end
end
-@spec update(id(), params()) ::
- {:ok, Intent.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: Intent.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, Intent.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &Intent.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: i}} -> {:ok, i}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id()) :: {:ok, Intent.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: Intent.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id()) :: {:ok, Intent.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, &(&1.one))
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: i}} -> {:ok, i}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+@spec delete!(Schema.id) :: Intent.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
@spec preload(Intent.t(), :action | :input_of | :output_of
| :provider | :receiver
| :resource_inventoried_as | :resource_conforms_to
| :resource_quantity | :effort_quantity | :available_quantity
| :at_location | :published_in)
:: Intent.t()
-def preload(int, :action) do
- Action.preload(int, :action)
-end
-
-def preload(int, :input_of) do
- Repo.preload(int, :input_of)
-end
-
-def preload(int, :output_of) do
- Repo.preload(int, :output_of)
-end
-
-def preload(int, :provider) do
- Repo.preload(int, :provider)
-end
-
-def preload(int, :receiver) do
- Repo.preload(int, :receiver)
-end
-
-def preload(int, :resource_inventoried_as) do
- Repo.preload(int, :resource_inventoried_as)
-end
-
-def preload(int, :resource_conforms_to) do
- Repo.preload(int, :resource_conforms_to)
-end
-
-def preload(int, :resource_quantity) do
- Measure.preload(int, :resource_quantity)
-end
-
-def preload(int, :effort_quantity) do
- Measure.preload(int, :effort_quantity)
-end
-
-def preload(int, :available_quantity) do
- Measure.preload(int, :available_quantity)
-end
-
-def preload(int, :at_location) do
- Repo.preload(int, :at_location)
-end
-
-def preload(int, :published_in) do
- Repo.preload(int, :published_in)
+def preload(int, x) when x in ~w[
+ input_of output_of provider receiver resource_inventoried_as
+ resource_conforms_to at_location published_in
+]a,
+ do: Repo.preload(int, x)
+def preload(int, :action), do: Action.preload(int, :action)
+def preload(int, x) when x in ~w[
+ effort_quantity available_quantity resource_quantity
+]a,
+ do: Measure.preload(int, x)
+
+@spec multi_key() :: atom()
+def multi_key(), do: :intent
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, Intent.changeset(params))
+end
+
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &Intent.changeset(Map.fetch!(&1, "#{key}.one"), params))
+end
+
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
end
end
diff --git a/src/zenflows/vf/intent/resolv.ex b/src/zenflows/vf/intent/resolv.ex
@@ -16,8 +16,9 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Intent.Resolv do
-@moduledoc "Resolvers of Intent."
+@moduledoc false
+alias Zenflows.GQL.Connection
alias Zenflows.VF.Intent.Domain
def intent(params, _) do
@@ -25,7 +26,10 @@ def intent(params, _) do
end
def intents(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_intent(%{intent: params}, _) do
diff --git a/src/zenflows/vf/intent/type.ex b/src/zenflows/vf/intent/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Intent.Type do
-@moduledoc "GraphQL types of Intents."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/measure.ex b/src/zenflows/vf/measure.ex
@@ -23,6 +23,8 @@ unit.
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.Schema
alias Zenflows.VF.Unit
@type t() :: %__MODULE__{
@@ -45,38 +47,35 @@ and `Zenflows.VF.ScenarioDefinition` modules.
def cast(cset, key) do
case Changeset.fetch_change(cset, key) do
{:ok, params} ->
- case chgset(params) do
+ case changeset(params) do
%{valid?: true} = cset_meas ->
cset
- |> Changeset.put_change(field_has_unit_id(key),
+ |> Changeset.put_change(:"#{key}_has_unit_id",
Changeset.fetch_change!(cset_meas, :has_unit_id))
- |> Changeset.put_change(field_has_numerical_value(key),
+ |> Changeset.put_change(:"#{key}_has_numerical_value",
Changeset.fetch_change!(cset_meas, :has_numerical_value))
-
cset_meas ->
Changeset.traverse_errors(cset_meas, fn {msg, opts} ->
Regex.replace(~r"%{(\w+)}", msg, fn _, key ->
- opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
+ opts |> Keyword.get(:"#{key}", key) |> to_string()
end)
end)
|> Enum.reduce(cset, fn {field, msg}, acc ->
Changeset.add_error(acc, key, "#{field}: #{msg}")
end)
end
-
:error ->
# Ecto seems to convert the params' keys to string
# whether they were originally string or atom.
- strkey = Atom.to_string(key)
+ strkey = "#{key}"
case cset.params do
# If the field `key` is set to `nil`,
# this will set the associated fields to
# `nil` as well.
%{^strkey => nil} ->
cset
- |> Changeset.force_change(field_has_unit_id(key), nil)
- |> Changeset.force_change(field_has_numerical_value(key), nil)
-
+ |> Changeset.force_change(:"#{key}_has_unit_id", nil)
+ |> Changeset.force_change(:"#{key}_has_numerical_value", nil)
_ ->
cset
end
@@ -84,8 +83,7 @@ def cast(cset, key) do
|> case do
# if not embedded schema, which doesn't allow `assoc_constraint/3`...
%{data: %{__meta__: %Ecto.Schema.Metadata{}}} = cset ->
- Changeset.assoc_constraint(cset, String.to_existing_atom("#{key}_has_unit"))
-
+ Changeset.assoc_constraint(cset, :"#{key}_has_unit")
# this case is most useful when testing, which you use emebedded schemas
cset ->
cset
@@ -100,29 +98,19 @@ as a %Measure{} struct. Useful for GraphQL types as can be seen in
@spec preload(Schema.t(), atom()) :: Schema.t()
def preload(schema, key) do
%{schema | key => %__MODULE__{
- has_unit_id: Map.get(schema, field_has_unit_id(key)),
- has_numerical_value: Map.get(schema, field_has_numerical_value(key)),
+ has_unit_id: Map.get(schema, :"#{key}_has_unit_id"),
+ has_numerical_value: Map.get(schema, :"#{key}_has_numerical_value"),
}}
end
@cast ~w[has_unit_id has_numerical_value]a
@reqr @cast
-@spec chgset(params()) :: Changeset.t()
-defp chgset(params) do
+@spec changeset(Schema.params()) :: Changeset.t()
+defp changeset(params) do
%__MODULE__{}
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
|> Changeset.validate_number(:has_numerical_value, greater_than: 0)
end
-
-@spec field_has_unit_id(atom()) :: atom()
-defp field_has_unit_id(key) do
- String.to_existing_atom("#{key}_has_unit_id")
-end
-
-@spec field_has_numerical_value(atom()) :: atom()
-defp field_has_numerical_value(key) do
- String.to_existing_atom("#{key}_has_numerical_value")
-end
end
diff --git a/src/zenflows/vf/measure/resolv.ex b/src/zenflows/vf/measure/resolv.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Measure.Resolv do
-@moduledoc "Resolvers of Measures."
+@moduledoc false
alias Zenflows.VF.{Measure, Measure.Domain}
diff --git a/src/zenflows/vf/measure/type.ex b/src/zenflows/vf/measure/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Measure.Type do
-@moduledoc "GraphQL types of Measures."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/organization.ex b/src/zenflows/vf/organization.ex
@@ -20,8 +20,10 @@ defmodule Zenflows.VF.Organization do
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.File
-alias Zenflows.VF.{SpatialThing, Validate}
+alias Zenflows.VF.SpatialThing
@type t() :: %__MODULE__{
type: :org,
@@ -47,14 +49,14 @@ end
@cast @reqr ++ ~w[classified_as note primary_location_id]a
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
|> Validate.name(:name)
|> Validate.note(:note)
- |> Changeset.cast_assoc(:images, with: &File.chgset/2)
+ |> Changeset.cast_assoc(:images)
|> Validate.class(:classified_as)
|> Changeset.assoc_constraint(:primary_location)
end
diff --git a/src/zenflows/vf/organization/domain.ex b/src/zenflows/vf/organization/domain.ex
@@ -18,80 +18,128 @@
defmodule Zenflows.VF.Organization.Domain do
@moduledoc "Domain logic of Organizations."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.{Organization, Organization.Filter}
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, Organization.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
def one(repo, clauses) do
- clauses = if(is_map(clauses),
- do: Map.put(clauses, :type, :org),
- else: Keyword.put(clauses, :type, :org))
- case repo.get_by(Organization, clauses) do
+ import Ecto.Query
+
+ case repo.get_by(where(Organization, type: :org), clauses) do
nil -> {:error, "not found"}
found -> {:ok, found}
end
end
-@spec all(Paging.params()) :: Filter.error() | Paging.result()
-def all(params \\ %{}) do
- with {:ok, q} <- Filter.filter(params[:filter] || %{}) do
- Paging.page(q, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: Organization.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [Organization.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ with {:ok, q} <- Filter.all(page) do
+ {:ok, Page.all(q, page)}
end
end
-@spec create(params()) :: {:ok, Organization.t()} | {:error, chgset()}
+@spec all!(Page.t()) :: [Organization.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
+end
+
+@spec create(Schema.params()) :: {:ok, Organization.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, Organization.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: o}} -> {:ok, o}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params())
- :: {:ok, Organization.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: Organization.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, Organization.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &Organization.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: o}} -> {:ok, o}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id()) :: {:ok, Organization.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: Organization.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id())
+ :: {:ok, Organization.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, &(&1.one))
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: o}} -> {:ok, o}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+@spec delete!(Schema.id()) :: Organization.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
@spec preload(Organization.t(), :images | :primary_location) :: Organization.t()
-def preload(org, :images) do
- Repo.preload(org, :images)
+def preload(org, x) when x in ~w[images primary_location]a do
+ Repo.preload(org, x)
+end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :organization
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, Organization.changeset(params))
+end
+
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &Organization.changeset(Map.fetch!(&1, "#{key}.one"), params))
end
-def preload(org, :primary_location) do
- Repo.preload(org, :primary_location)
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
end
end
diff --git a/src/zenflows/vf/organization/filter.ex b/src/zenflows/vf/organization/filter.ex
@@ -16,40 +16,33 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Organization.Filter do
-@moduledoc "Filtering logic of Organizations."
-
-use Zenflows.DB.Schema
+@moduledoc false
import Ecto.Query
-alias Ecto.Query
-alias Zenflows.DB.Filter
-alias Zenflows.VF.{Organization, Validate}
-
-@type error() :: Filter.error()
+alias Ecto.{Changeset, Queryable}
+alias Zenflows.DB.{Page, Schema, Validate}
+alias Zenflows.VF.Organization
-@spec filter(Filter.params()) :: Filter.result()
-def filter(params) do
- case chgset(params) do
- %{valid?: true, changes: c} ->
- {:ok, Enum.reduce(c, where(Organization, type: :org), &f(&2, &1))}
- %{valid?: false} = cset ->
- {:error, cset}
+@spec all(Page.t()) :: {:ok, Queryable.t()} | {:error, Changeset.t()}
+def all(%{filter: nil}), do: {:ok, where(Organization, type: :org)}
+def all(%{filter: params}) do
+ with {:ok, filters} <- all_validate(params) do
+ Enum.reduce(filters, where(Organization, type: :org), &all_f(&2, &1))
end
end
-@spec f(Query.t(), {atom(), term()}) :: Query.t()
-defp f(q, {:name, v}),
- do: where(q, [x], ilike(x.name, ^"%#{Filter.escape_like(v)}%"))
-
-embedded_schema do
- field :name, :string
-end
+@spec all_f(Queryable.t(), {atom(), term()}) :: Queryable.t()
+defp all_f(q, {:name, v}),
+ do: where(q, [x], ilike(x.name, ^"%#{v}%"))
-@spec chgset(params()) :: Changeset.t()
-defp chgset(params) do
- %__MODULE__{}
+@spec all_validate(Schema.params())
+ :: {:ok, Changeset.data()} | {:error, Changeset.t()}
+defp all_validate(params) do
+ {%{}, %{name: :string}}
|> Changeset.cast(params, [:name])
|> Validate.name(:name)
+ |> Validate.escape_like(:name)
+ |> Changeset.apply_action(nil)
end
end
diff --git a/src/zenflows/vf/organization/resolv.ex b/src/zenflows/vf/organization/resolv.ex
@@ -16,8 +16,9 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Organization.Resolv do
-@moduledoc "Resolvers of Organizations."
+@moduledoc false
+alias Zenflows.GQL.Connection
alias Zenflows.VF.Organization.Domain
def organization(params, _) do
@@ -25,7 +26,10 @@ def organization(params, _) do
end
def organizations(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_organization(%{organization: params}, _) do
diff --git a/src/zenflows/vf/organization/type.ex b/src/zenflows/vf/organization/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Organization.Type do
-@moduledoc "GraphQL types of Organizations."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/person.ex b/src/zenflows/vf/person.ex
@@ -20,8 +20,10 @@ defmodule Zenflows.VF.Person do
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.File
-alias Zenflows.VF.{SpatialThing, Validate}
+alias Zenflows.VF.SpatialThing
@type t() :: %__MODULE__{
type: :per,
@@ -69,22 +71,22 @@ end
# insert changeset
@doc false
-@spec chgset(params()) :: Changeset.t()
-def chgset(params) do
+@spec changeset(Schema.params()) :: Changeset.t()
+def changeset(params) do
%__MODULE__{}
|> Changeset.cast(params, @insert_cast)
|> Changeset.validate_required(@insert_reqr)
|> Validate.name(:name)
|> Validate.name(:user)
|> Validate.name(:email)
- |> Changeset.cast_assoc(:images, with: &File.chgset/2)
+ |> Changeset.cast_assoc(:images)
|> Validate.note(:note)
|> Validate.key(:ecdh_public_key)
|> Validate.key(:eddsa_public_key)
|> Validate.key(:ethereum_address)
|> Validate.key(:reflow_public_key)
|> Validate.key(:schnorr_public_key)
- |> check_email()
+ |> Validate.email(:email)
|> Changeset.unique_constraint(:user)
|> Changeset.unique_constraint(:name)
|> Changeset.unique_constraint(:email)
@@ -93,23 +95,16 @@ end
# update changeset
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema, params) do
schema
|> Changeset.cast(params, @update_cast)
|> Validate.name(:name)
|> Validate.name(:user)
|> Validate.note(:note)
- |> check_email()
+ |> Validate.email(:email)
|> Changeset.unique_constraint(:user)
|> Changeset.unique_constraint(:name)
|> Changeset.assoc_constraint(:primary_location)
end
-
-# Validate that :email is a valid email address.
-@spec check_email(Changeset.t()) :: Changeset.t()
-defp check_email(cset) do
- # works good enough for now
- Changeset.validate_format(cset, :email, ~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])?)*$/)
-end
end
diff --git a/src/zenflows/vf/person/domain.ex b/src/zenflows/vf/person/domain.ex
@@ -20,44 +20,48 @@ defmodule Zenflows.VF.Person.Domain do
import Ecto.Query
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.{Person, Person.Filter}
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t()) :: {:ok, Person.t()} | {:error, String.t()}
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
+ :: {:ok, Person.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
def one(repo, clauses) do
- clauses = if(is_map(clauses),
- do: Map.put(clauses, :type, :per),
- else: Keyword.put(clauses, :type, :per))
- case repo.get_by(Person, clauses) do
+ case repo.get_by(where(Person, type: :per), clauses) do
nil -> {:error, "not found"}
found -> {:ok, found}
end
end
-@spec all(Paging.params()) :: Filter.error() | Paging.result()
-def all(params \\ %{}) do
- with {:ok, q} <- Filter.filter(params[:filter] || %{}) do
- Paging.page(q, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: Person.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [Person.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ with {:ok, q} <- Filter.all(page) do
+ {:ok, Page.all(q, page)}
end
end
+@spec all!(Page.t()) :: [Person.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
+end
+
@spec exists?(Keyword.t()) :: boolean()
def exists?(conds) do
where(Person, ^conds) |> where(type: :per) |> Repo.exists?()
end
-@spec pubkey(id()) :: {:ok, String.t()} | {:error, String.t()}
+@spec pubkey(Schema.id()) :: {:ok, nil | String.t()} | {:error, String.t()}
def pubkey(id) do
- where(Person, id: ^id)
- |> where(type: :per)
+ where(Person, type: :per, id: ^id)
|> select([:eddsa_public_key]) |> Repo.one()
|> case do
nil -> {:error, "not found"}
@@ -65,50 +69,92 @@ def pubkey(id) do
end
end
-@spec create(params()) :: {:ok, Person.t()} | {:error, chgset()}
+@spec create(Schema.params()) :: {:ok, Person.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, Person.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: p}} -> {:ok, p}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params())
- :: {:ok, Person.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: Person.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, Person.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &Person.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: p}} -> {:ok, p}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id()) :: {:ok, Person.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: Person.t()
+def update!(id, params) do
+ {:ok, value} = __MODULE__.update(id, params)
+ value
+end
+
+@spec delete(Schema.id()) ::
+ {:ok, Person.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, &(&1.one))
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: p}} -> {:ok, p}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+@spec delete!(Schema.id()) :: Person.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
@spec preload(Person.t(), :images | :primary_location) :: Person.t()
-def preload(per, :images) do
- Repo.preload(per, :images)
+def preload(per, x) when x in ~w[images primary_location]a do
+ Repo.preload(per, x)
+end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :person
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, Person.changeset(params))
+end
+
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &Person.changeset(Map.fetch!(&1, "#{key}.one"), params))
end
-def preload(per, :primary_location) do
- Repo.preload(per, :primary_location)
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
end
end
diff --git a/src/zenflows/vf/person/filter.ex b/src/zenflows/vf/person/filter.ex
@@ -16,56 +16,47 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Person.Filter do
-@moduledoc "Filtering logic of Persons."
-
-use Zenflows.DB.Schema
+@moduledoc false
import Ecto.Query
-alias Ecto.Query
-alias Zenflows.DB.Filter
-alias Zenflows.VF.{Person, Validate}
-
-@type error() :: Filter.error()
+alias Ecto.{Changeset, Queryable}
+alias Zenflows.DB.{Page, Schema, Validate}
+alias Zenflows.VF.Person
-@spec filter(Filter.params()) :: Filter.result()
-def filter(params) do
- case chgset(params) do
- %{valid?: true, changes: c} ->
- {:ok, Enum.reduce(c, where(Person, type: :per), &f(&2, &1))}
- %{valid?: false} = cset ->
- {:error, cset}
+@spec all(Page.t()) :: {:ok, Queryable.t()} | {:error, Changeset.t()}
+def all(%{filter: nil}), do: {:ok, where(Person, type: :per)}
+def all(%{filter: params}) do
+ with {:ok, filters} <- all_validate(params) do
+ Enum.reduce(filters, where(Person, type: :per), &all_f(&2, &1))
end
end
-@spec f(Query.t(), {atom(), term()}) :: Query.t()
-defp f(q, {:name, v}),
- do: where(q, [x], ilike(x.name, ^"%#{Filter.escape_like(v)}%"))
-defp f(q, {:or_name, v}),
- do: or_where(q, [x], ilike(x.name, ^"%#{Filter.escape_like(v)}%"))
-defp f(q, {:user, v}),
- do: where(q, [x], ilike(x.user, ^"%#{Filter.escape_like(v)}%"))
-defp f(q, {:or_user, v}),
- do: or_where(q, [x], ilike(x.user, ^"%#{Filter.escape_like(v)}%"))
-
-embedded_schema do
- field :name, :string
- field :or_name, :string
- field :user, :string
- field :or_user, :string
-end
-
-@cast ~w[name or_name user or_user]a
-
-@spec chgset(params()) :: Changeset.t()
-defp chgset(params) do
- %__MODULE__{}
- |> Changeset.cast(params, @cast)
+@spec all_f(Queryable.t(), {atom(), term()}) :: Queryable.t()
+defp all_f(q, {:name, v}),
+ do: where(q, [x], ilike(x.name, ^"%#{v}%"))
+defp all_f(q, {:or_name, v}),
+ do: or_where(q, [x], ilike(x.name, ^"%#{v}%"))
+defp all_f(q, {:user, v}),
+ do: where(q, [x], ilike(x.user, ^"%#{v}%"))
+defp all_f(q, {:or_user, v}),
+ do: or_where(q, [x], ilike(x.user, ^"%#{v}"))
+
+@spec all_validate(Schema.params()) ::
+ {:ok, Changeset.data()} | {:error, Changeset.t()}
+defp all_validate(params) do
+ {%{}, %{name: :string, or_name: :string, user: :string, or_user: :string}}
+ |> Changeset.cast(params, ~w[name or_name user or_user]a)
|> Validate.name(:name)
|> Validate.name(:or_name)
|> Validate.name(:user)
|> Validate.name(:or_user)
- |> Filter.check_xor(:name, :or_name)
- |> Filter.check_xor(:user, :or_user)
+ |> Validate.exist_xor([:name, :or_name])
+ |> Validate.exist_xor([:user, :or_user])
+ |> Validate.escape_like(:name)
+ |> Validate.escape_like(:or_name)
+ |> Validate.escape_like(:user)
+ |> Validate.escape_like(:or_user)
+ |> Changeset.apply_action(nil)
end
end
diff --git a/src/zenflows/vf/person/resolv.ex b/src/zenflows/vf/person/resolv.ex
@@ -16,8 +16,11 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Person.Resolv do
-@moduledoc "Resolvers of Persons."
+@moduledoc false
+alias Ecto.Changeset
+alias Zenflows.DB.Validate
+alias Zenflows.GQL.Connection
alias Zenflows.VF.Person.Domain
def person(params, _) do
@@ -25,7 +28,16 @@ def person(params, _) do
end
def people(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
+end
+
+def person_exists(params, _) do
+ with {:ok, parsed} <- parse_person_exists(params) do
+ {:ok, parsed |> Map.to_list() |> Domain.exists?()}
+ end
end
def person_check(params, _) do
@@ -63,4 +75,14 @@ def primary_location(per, _, _) do
per = Domain.preload(per, :primary_location)
{:ok, per.primary_location}
end
+
+@spec parse_person_exists(map()) :: {:ok, map()} | {:error, Changeset.t()}
+def parse_person_exists(params) do
+ {%{}, %{email: :string, user: :string}}
+ |> Changeset.cast(params, [:email, :user])
+ |> Validate.exist_xor([:email, :user])
+ |> Validate.email(:email)
+ |> Validate.name(:user)
+ |> Changeset.apply_action(nil)
+end
end
diff --git a/src/zenflows/vf/person/type.ex b/src/zenflows/vf/person/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Person.Type do
-@moduledoc "GraphQL types of Persons."
+@moduledoc false
use Absinthe.Schema.Notation
@@ -180,6 +180,14 @@ object :query_person do
resolve &Resolv.people/2
end
+ @desc "Check if a person exists by email xor username."
+ field :person_exists, non_null(:boolean) do
+ meta only_guest?: true
+ arg :email, :string
+ arg :user, :string
+ resolve &Resolv.person_exists/2
+ end
+
@desc "If exists, find a person by email and eddsa-public-key."
field :person_check, non_null(:person) do
meta only_guest?: true
diff --git a/src/zenflows/vf/plan.ex b/src/zenflows/vf/plan.ex
@@ -23,7 +23,9 @@ with defined deliverable(s).
use Zenflows.DB.Schema
-alias Zenflows.VF.{Scenario, Validate}
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
+alias Zenflows.VF.Scenario
@type t() :: %__MODULE__{
name: String.t(),
@@ -44,8 +46,8 @@ end
@cast @reqr ++ ~w[due note refinement_of_id]a
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/plan/domain.ex b/src/zenflows/vf/plan/domain.ex
@@ -18,16 +18,11 @@
defmodule Zenflows.VF.Plan.Domain do
@moduledoc "Domain logic of Plans."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.Plan
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, Plan.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -38,51 +33,108 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(Plan, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: Plan.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [Plan.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(Plan, page)}
+end
+
+@spec all!(Page.t()) :: [Plan.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
end
-@spec create(params()) :: {:ok, Plan.t()} | {:error, chgset()}
+@spec create(Schema.params()) :: {:ok, Plan.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, Plan.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: p}} -> {:ok, p}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params()) ::
- {:ok, Plan.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: Plan.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, Plan.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &Plan.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: p}} -> {:ok, p}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id()) :: {:ok, Plan.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: Plan.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id()) :: {:ok, Plan.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, & &1.one)
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: p}} -> {:ok, p}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+@spec delete!(Schema.id()) :: Plan.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
@spec preload(Plan.t(), :refinement_of) :: Plan.t()
def preload(plan, :refinement_of) do
Repo.preload(plan, :refinement_of)
end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :plan
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, Plan.changeset(params))
+end
+
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &Plan.changeset(Map.fetch!(&1, "#{key}.one"), params))
+end
+
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
+end
end
diff --git a/src/zenflows/vf/plan/resolv.ex b/src/zenflows/vf/plan/resolv.ex
@@ -16,10 +16,11 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Plan.Resolv do
-@moduledoc "Resolvers of Plan."
+@moduledoc false
use Absinthe.Schema.Notation
+alias Zenflows.GQL.Connection
alias Zenflows.VF.Plan.Domain
def plan(params, _) do
@@ -27,7 +28,10 @@ def plan(params, _) do
end
def plans(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_plan(%{plan: params}, _) do
diff --git a/src/zenflows/vf/plan/type.ex b/src/zenflows/vf/plan/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Plan.Type do
-@moduledoc "GraphQL types of Plans."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/process.ex b/src/zenflows/vf/process.ex
@@ -23,12 +23,13 @@ transport economic resource(s).
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.VF.{
EconomicEvent,
Plan,
ProcessSpecification,
Scenario,
- Validate,
}
@type t() :: %__MODULE__{
@@ -70,8 +71,8 @@ end
]a # in_scope_of_id
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/process/domain.ex b/src/zenflows/vf/process/domain.ex
@@ -18,16 +18,11 @@
defmodule Zenflows.VF.Process.Domain do
@moduledoc "Domain logic of Processes."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.Process
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, Process.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -38,60 +33,109 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(Process, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: Process.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [Process.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(Process, page)}
+end
+
+@spec all!(Page.t()) :: [Process.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
end
-@spec create(params()) :: {:ok, Process.t()} | {:error, chgset()}
+@spec create(Schema.params()) :: {:ok, Process.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, Process.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: p}} -> {:ok, p}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params())
- :: {:ok, Process.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: Process.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, Process.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &Process.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: p}} -> {:ok, p}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id()) :: {:ok, Process.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: Process.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id()) :: {:ok, Process.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, & &1.one)
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: p}} -> {:ok, p}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+@spec delete!(Schema.id()) :: Process.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
@spec preload(Process.t(), :based_on | :planned_within | :nested_in)
:: Process.t()
-def preload(proc, :based_on) do
- Repo.preload(proc, :based_on)
+def preload(proc, x) when x in ~w[based_on planned_within nested_in]a do
+ Repo.preload(proc, x)
+end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :process
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, Process.changeset(params))
end
-def preload(proc, :planned_within) do
- Repo.preload(proc, :planned_within)
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &Process.changeset(Map.fetch!(&1, "#{key}.one"), params))
end
-def preload(proc, :nested_in) do
- Repo.preload(proc, :nested_in)
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
end
end
diff --git a/src/zenflows/vf/process/resolv.ex b/src/zenflows/vf/process/resolv.ex
@@ -16,10 +16,11 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Process.Resolv do
-@moduledoc "Resolvers of Process."
+@moduledoc false
use Absinthe.Schema.Notation
+alias Zenflows.GQL.Connection
alias Zenflows.VF.Process.Domain
def process(params, _) do
@@ -27,7 +28,10 @@ def process(params, _) do
end
def processes(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_process(%{process: params}, _) do
diff --git a/src/zenflows/vf/process/type.ex b/src/zenflows/vf/process/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Process.Type do
-@moduledoc "GraphQL types of Processs."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/process_specification.ex b/src/zenflows/vf/process_specification.ex
@@ -22,7 +22,8 @@ Specifies the kind of process.
use Zenflows.DB.Schema
-alias Zenflows.VF.Validate
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
@type t() :: %__MODULE__{
name: String.t(),
@@ -39,8 +40,8 @@ end
@cast @reqr ++ [:note]
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/process_specification/domain.ex b/src/zenflows/vf/process_specification/domain.ex
@@ -18,16 +18,11 @@
defmodule Zenflows.VF.ProcessSpecification.Domain do
@moduledoc "Domain logic of ProcessSpecifications."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.ProcessSpecification
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, ProcessSpecification.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -38,47 +33,106 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(ProcessSpecification, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
+ :: ProcessSpecification.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [ProcessSpecification.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(ProcessSpecification, page)}
+end
+
+@spec all!(Page.t()) :: [ProcessSpecification.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
end
-@spec create(params()) :: {:ok, ProcessSpecification.t()} | {:error, chgset()}
+@spec create(Schema.params())
+ :: {:ok, ProcessSpecification.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, ProcessSpecification.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: p}} -> {:ok, p}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params())
- :: {:ok, ProcessSpecification.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: ProcessSpecification.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, ProcessSpecification.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &ProcessSpecification.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: p}} -> {:ok, p}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id())
- :: {:ok, ProcessSpecification.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: ProcessSpecification.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id())
+ :: {:ok, ProcessSpecification.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, & &1.one)
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: p}} -> {:ok, p}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+
+@spec delete!(Schema.id()) :: ProcessSpecification.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :process_specification
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, ProcessSpecification.changeset(params))
+end
+
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &ProcessSpecification.changeset(Map.fetch!(&1, "#{key}.one"), params))
+end
+
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
+end
end
diff --git a/src/zenflows/vf/process_specification/resolv.ex b/src/zenflows/vf/process_specification/resolv.ex
@@ -16,8 +16,9 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.ProcessSpecification.Resolv do
-@moduledoc "Resolvers of ProcessSpecifications."
+@moduledoc false
+alias Zenflows.GQL.Connection
alias Zenflows.VF.ProcessSpecification.Domain
def process_specification(params, _) do
@@ -25,7 +26,10 @@ def process_specification(params, _) do
end
def process_specifications(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_process_specification(%{process_specification: params}, _) do
diff --git a/src/zenflows/vf/process_specification/type.ex b/src/zenflows/vf/process_specification/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.ProcessSpecification.Type do
-@moduledoc "GraphQL types of ProcessSpecifications."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/product_batch.ex b/src/zenflows/vf/product_batch.ex
@@ -23,7 +23,8 @@ same way.
use Zenflows.DB.Schema
-alias Zenflows.VF.Validate
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
@type t() :: %__MODULE__{
batch_number: String.t(),
@@ -42,8 +43,8 @@ end
@cast @reqr ++ ~w[expiry_date production_date]a
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/product_batch/domain.ex b/src/zenflows/vf/product_batch/domain.ex
@@ -18,16 +18,11 @@
defmodule Zenflows.VF.ProductBatch.Domain do
@moduledoc "Domain logic of ProductBatches."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.ProductBatch
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, ProductBatch.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -38,46 +33,103 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(ProductBatch, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: ProductBatch.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [ProductBatch.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(ProductBatch, page)}
+end
+
+@spec all!(Page.t()) :: [ProductBatch.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
end
-@spec create(params()) :: {:ok, ProductBatch.t()} | {:error, chgset()}
+@spec create(Schema.params()) :: {:ok, ProductBatch.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, ProductBatch.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: pb}} -> {:ok, pb}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params())
- :: {:ok, ProductBatch.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: ProductBatch.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, ProductBatch.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &ProductBatch.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: pb}} -> {:ok, pb}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id()) :: {:ok, ProductBatch.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: ProductBatch.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id()) :: {:ok, ProductBatch.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, & &1.one)
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: pb}} -> {:ok, pb}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+
+@spec delete!(Schema.id()) :: ProductBatch.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :product_batch
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, ProductBatch.changeset(params))
+end
+
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &ProductBatch.changeset(Map.fetch!(&1, "#{key}.one"), params))
+end
+
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
+end
end
diff --git a/src/zenflows/vf/product_batch/resolv.ex b/src/zenflows/vf/product_batch/resolv.ex
@@ -16,10 +16,11 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.ProductBatch.Resolv do
-@moduledoc "Resolvers of ProductBatch."
+@moduledoc false
use Absinthe.Schema.Notation
+alias Zenflows.GQL.Connection
alias Zenflows.VF.ProductBatch.Domain
def product_batch(params, _) do
@@ -27,7 +28,10 @@ def product_batch(params, _) do
end
def product_batches(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_product_batch(%{product_batch: params}, _) do
diff --git a/src/zenflows/vf/product_batch/type.ex b/src/zenflows/vf/product_batch/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.ProductBatch.Type do
-@moduledoc "GraphQL types of ProductBatches."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/proposal.ex b/src/zenflows/vf/proposal.ex
@@ -22,11 +22,12 @@ Published requests or offers, sometimes with what is expected in return.
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.VF.{
Intent,
ProposedIntent,
SpatialThing,
- Validate,
}
@type t() :: %__MODULE__{
@@ -60,8 +61,8 @@ end
@cast ~w[name has_beginning has_end unit_based note eligible_location_id]a
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Validate.name(:name)
diff --git a/src/zenflows/vf/proposal/domain.ex b/src/zenflows/vf/proposal/domain.ex
@@ -18,16 +18,11 @@
defmodule Zenflows.VF.Proposal.Domain do
@moduledoc "Domain logic of Proposals."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.{Proposal, Proposal.Filter}
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, Proposal.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -38,68 +33,115 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Filter.error() | Paging.result()
-def all(params \\ %{}) do
- with {:ok, q} <- Filter.filter(params[:filter] || %{}) do
- Paging.page(q, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: Proposal.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [Proposal.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ with {:ok, q} <- Filter.all(page) do
+ {:ok, Page.all(q, page)}
end
end
-@spec create(params()) :: {:ok, Proposal.t()} | {:error, chgset()}
+@spec all!(Page.t()) :: [Proposal.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
+end
+
+@spec create(Schema.params()) :: {:ok, Proposal.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, Proposal.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: p}} -> {:ok, p}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params()) ::
- {:ok, Proposal.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: Proposal.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, Proposal.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &Proposal.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: p}} -> {:ok, p}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id())
- :: {:ok, Proposal.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: Proposal.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id())
+ :: {:ok, Proposal.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, & &1.one)
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: pi}} -> {:ok, pi}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+@spec delete!(Schema.id()) :: Proposal.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
@spec preload(Proposal.t(), :eligible_location | :publishes
| :primary_intents | :reciprocal_intents)
:: Proposal.t()
-def preload(prop, :eligible_location) do
- Repo.preload(prop, :eligible_location)
+def preload(prop, x) when x in ~w[
+ eligible_location publishes primary_intents reciprocal_intents
+]a do
+ Repo.preload(prop, x)
+end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :proposal
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
end
-def preload(prop, :publishes) do
- Repo.preload(prop, :publishes)
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, Proposal.changeset(params))
end
-def preload(prop, :primary_intents) do
- Repo.preload(prop, :primary_intents)
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &Proposal.changeset(Map.fetch!(&1, "#{key}.one"), params))
end
-def preload(prop, :reciprocal_intents) do
- Repo.preload(prop, :reciprocal_intents)
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
end
end
diff --git a/src/zenflows/vf/proposal/filter.ex b/src/zenflows/vf/proposal/filter.ex
@@ -16,81 +16,76 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Proposal.Filter do
-@moduledoc "Filtering logic of Proposals."
-
-use Zenflows.DB.Schema
+@moduledoc false
import Ecto.Query
-alias Ecto.Query
-alias Zenflows.DB.{Filter, ID}
-alias Zenflows.VF.{Proposal, Validate}
-
-@type error() :: Filter.error()
+alias Ecto.{Changeset, Queryable}
+alias Zenflows.DB.{ID, Page, Schema, Validate}
+alias Zenflows.VF.Proposal
-@spec filter(Filter.params()) :: Filter.result()
-def filter(params) do
- case chgset(params) do
- %{valid?: true, changes: c} ->
- {:ok, Enum.reduce(c, Proposal, &f(&2, &1))}
- %{valid?: false} = cset ->
- {:error, cset}
+@spec all(Page.t()) :: {:ok, Queryable.t()} | {:error, Changeset.t()}
+def all(%{filter: nil}), do: {:ok, Proposal}
+def all(%{filter: params}) do
+ with {:ok, filters} <- all_validate(params) do
+ Enum.reduce(filters, Proposal, &all_f(&2, &1))
end
end
-@spec f(Query.t(), {atom(), term()}) :: Query.t()
-defp f(q, {:primary_intents_resource_inventoried_as_conforms_to, v}) do
+@spec all_f(Queryable.t(), {atom(), term()}) :: Queryable.t()
+defp all_f(q, {:primary_intents_resource_inventoried_as_conforms_to, v}) do
q
|> join(:primary_intents_resource_inventoried_as)
|> where([primary_intents_resource_inventoried_as: r], r.conforms_to_id in ^v)
end
-defp f(q, {:or_primary_intents_resource_inventoried_as_conforms_to, v}) do
+defp all_f(q, {:or_primary_intents_resource_inventoried_as_conforms_to, v}) do
q
|> join(:primary_intents_resource_inventoried_as)
|> or_where([primary_intents_resource_inventoried_as: r], r.conforms_to_id in ^v)
end
-defp f(q, {:primary_intents_resource_inventoried_as_primary_accountable, v}) do
+defp all_f(q, {:primary_intents_resource_inventoried_as_primary_accountable, v}) do
q
|> join(:primary_intents_resource_inventoried_as)
|> where([primary_intents_resource_inventoried_as: r], r.primary_accountable_id in ^v)
end
-defp f(q, {:or_primary_intents_resource_inventoried_as_primary_accountable, v}) do
+defp all_f(q, {:or_primary_intents_resource_inventoried_as_primary_accountable, v}) do
q
|> join(:primary_intents_resource_inventoried_as)
|> or_where([primary_intents_resource_inventoried_as: r], r.primary_accountable_id in ^v)
end
-defp f(q, {:primary_intents_resource_inventoried_as_classified_as, v}) do
+defp all_f(q, {:primary_intents_resource_inventoried_as_classified_as, v}) do
q
|> join(:primary_intents_resource_inventoried_as)
|> where([primary_intents_resource_inventoried_as: r], fragment("? @> ?", r.classified_as, ^v))
end
-defp f(q, {:or_primary_intents_resource_inventoried_as_classified_as, v}) do
+defp all_f(q, {:or_primary_intents_resource_inventoried_as_classified_as, v}) do
q
|> join(:primary_intents_resource_inventoried_as)
|> or_where([primary_intents_resource_inventoried_as: r], fragment("? @> ?", r.classified_as, ^v))
end
-defp f(q, {:primary_intents_resource_inventoried_as_name, v}) do
+defp all_f(q, {:primary_intents_resource_inventoried_as_name, v}) do
q
|> join(:primary_intents_resource_inventoried_as)
- |> where([primary_intents_resource_inventoried_as: r], ilike(r.name, ^"%#{Filter.escape_like(v)}%"))
+ |> where([primary_intents_resource_inventoried_as: r], ilike(r.name, ^"%#{v}%"))
end
-defp f(q, {:or_primary_intents_resource_inventoried_as_name, v}) do
+defp all_f(q, {:or_primary_intents_resource_inventoried_as_name, v}) do
q
|> join(:primary_intents_resource_inventoried_as)
- |> or_where([primary_intents_resource_inventoried_as: r], ilike(r.name, ^"%#{Filter.escape_like(v)}%"))
+ |> or_where([primary_intents_resource_inventoried_as: r], ilike(r.name, ^v))
end
-defp f(q, {:primary_intents_resource_inventoried_as_id, v}) do
+defp all_f(q, {:primary_intents_resource_inventoried_as_id, v}) do
q
|> join(:primary_intents_resource_inventoried_as)
|> where([primary_intents_resource_inventoried_as: r], r.id in ^v)
end
-defp f(q, {:or_primary_intents_resource_inventoried_as_id, v}) do
+defp all_f(q, {:or_primary_intents_resource_inventoried_as_id, v}) do
q
|> join(:primary_intents_resource_inventoried_as)
|> or_where([primary_intents_resource_inventoried_as: r], r.id in ^v)
end
# join primary_intents
+@spec join(Queryable.t(), atom()) :: Queryable.t()
defp join(q, :primary_intents) do
if has_named_binding?(q, :primary_intents),
do: q,
@@ -105,36 +100,33 @@ defp join(q, :primary_intents_resource_inventoried_as) do
as: :primary_intents_resource_inventoried_as)
end
-embedded_schema do
- field :primary_intents_resource_inventoried_as_conforms_to, {:array, ID}
- field :or_primary_intents_resource_inventoried_as_conforms_to, {:array, ID}
- field :primary_intents_resource_inventoried_as_primary_accountable, {:array, ID}
- field :or_primary_intents_resource_inventoried_as_primary_accountable, {:array, ID}
- field :primary_intents_resource_inventoried_as_classified_as, {:array, :string}
- field :or_primary_intents_resource_inventoried_as_classified_as, {:array, :string}
- field :primary_intents_resource_inventoried_as_name, :string
- field :or_primary_intents_resource_inventoried_as_name, :string
- field :primary_intents_resource_inventoried_as_id, {:array, ID}
- field :or_primary_intents_resource_inventoried_as_id, {:array, ID}
-end
-
-@cast ~w[
- primary_intents_resource_inventoried_as_conforms_to
- or_primary_intents_resource_inventoried_as_conforms_to
- primary_intents_resource_inventoried_as_primary_accountable
- or_primary_intents_resource_inventoried_as_primary_accountable
- primary_intents_resource_inventoried_as_classified_as
- or_primary_intents_resource_inventoried_as_classified_as
- primary_intents_resource_inventoried_as_name
- or_primary_intents_resource_inventoried_as_name
- primary_intents_resource_inventoried_as_id
- or_primary_intents_resource_inventoried_as_id
-]a
-
-@spec chgset(params()) :: Changeset.t()
-defp chgset(params) do
- %__MODULE__{}
- |> Changeset.cast(params, @cast)
+@spec all_validate(Schema.params())
+ :: {:ok, Changeset.data()} | {:error, Changeset.t()}
+defp all_validate(params) do
+ {%{}, %{
+ primary_intents_resource_inventoried_as_conforms_to: {:array, ID},
+ or_primary_intents_resource_inventoried_as_conforms_to: {:array, ID},
+ primary_intents_resource_inventoried_as_primary_accountable: {:array, ID},
+ or_primary_intents_resource_inventoried_as_primary_accountable: {:array, ID},
+ primary_intents_resource_inventoried_as_classified_as: {:array, :string},
+ or_primary_intents_resource_inventoried_as_classified_as: {:array, :string},
+ primary_intents_resource_inventoried_as_name: :string,
+ or_primary_intents_resource_inventoried_as_name: :string,
+ primary_intents_resource_inventoried_as_id: {:array, ID},
+ or_primary_intents_resource_inventoried_as_id: {:array, ID},
+ }}
+ |> Changeset.cast(params, ~w[
+ primary_intents_resource_inventoried_as_conforms_to
+ or_primary_intents_resource_inventoried_as_conforms_to
+ primary_intents_resource_inventoried_as_primary_accountable
+ or_primary_intents_resource_inventoried_as_primary_accountable
+ primary_intents_resource_inventoried_as_classified_as
+ or_primary_intents_resource_inventoried_as_classified_as
+ primary_intents_resource_inventoried_as_name
+ or_primary_intents_resource_inventoried_as_name
+ primary_intents_resource_inventoried_as_id
+ or_primary_intents_resource_inventoried_as_id
+ ]a)
|> Validate.class(:primary_intents_resource_inventoried_as_conforms_to)
|> Validate.class(:or_primary_intents_resource_inventoried_as_conforms_to)
|> Validate.class(:primary_intents_resource_inventoried_as_primary_accountable)
@@ -143,15 +135,18 @@ defp chgset(params) do
|> Validate.class(:or_primary_intents_resource_inventoried_as_classified_as)
|> Validate.name(:primary_intents_resource_inventoried_as_name)
|> Validate.name(:or_primary_intents_resource_inventoried_as_name)
- |> Filter.check_xor(:primary_intents_resource_inventoried_as_conforms_to,
- :or_primary_intents_resource_inventoried_as_conforms_to)
- |> Filter.check_xor(:primary_intents_resource_inventoried_as_primary_accountable,
- :or_primary_intents_resource_inventoried_as_primary_accountable)
- |> Filter.check_xor(:primary_intents_resource_inventoried_as_classified_as,
- :or_primary_intents_resource_inventoried_as_classified_as)
- |> Filter.check_xor(:primary_intents_resource_inventoried_as_name,
- :or_primary_intents_resource_inventoried_as_name)
- |> Filter.check_xor(:primary_intents_resource_inventoried_as_id,
- :or_primary_intents_resource_inventoried_as_id)
+ |> Validate.exist_xor([:primary_intents_resource_inventoried_as_conforms_to,
+ :or_primary_intents_resource_inventoried_as_conforms_to])
+ |> Validate.exist_xor([:primary_intents_resource_inventoried_as_primary_accountable,
+ :or_primary_intents_resource_inventoried_as_primary_accountable])
+ |> Validate.exist_xor([:primary_intents_resource_inventoried_as_classified_as,
+ :or_primary_intents_resource_inventoried_as_classified_as])
+ |> Validate.exist_xor([:primary_intents_resource_inventoried_as_name,
+ :or_primary_intents_resource_inventoried_as_name])
+ |> Validate.exist_xor([:primary_intents_resource_inventoried_as_id,
+ :or_primary_intents_resource_inventoried_as_id])
+ |> Validate.escape_like(:primary_intents_resource_inventoried_as_name)
+ |> Validate.escape_like(:or_primary_intents_resource_inventoried)
+ |> Changeset.apply_action(nil)
end
end
diff --git a/src/zenflows/vf/proposal/resolv.ex b/src/zenflows/vf/proposal/resolv.ex
@@ -16,8 +16,9 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Proposal.Resolv do
-@moduledoc "Resolvers of Proposal."
+@moduledoc false
+alias Zenflows.GQL.Connection
alias Zenflows.VF.Proposal.Domain
def proposal(params, _) do
@@ -25,7 +26,10 @@ def proposal(params, _) do
end
def proposals(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def offers(_params, _) do
diff --git a/src/zenflows/vf/proposal/type.ex b/src/zenflows/vf/proposal/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Proposal.Type do
-@moduledoc "GraphQL types of Proposals."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/proposed_intent.ex b/src/zenflows/vf/proposed_intent.ex
@@ -24,6 +24,8 @@ including multiple intents.
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.Schema
alias Zenflows.VF.{Intent, Proposal}
@type t() :: %__MODULE__{
@@ -43,8 +45,8 @@ end
@cast @reqr ++ [:reciprocal]
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/proposed_intent/domain.ex b/src/zenflows/vf/proposed_intent/domain.ex
@@ -18,16 +18,11 @@
defmodule Zenflows.VF.ProposedIntent.Domain do
@moduledoc "Domain logic of ProposedIntents."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.ProposedIntent
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, ProposedIntent.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -38,43 +33,109 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(ProposedIntent, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: ProposedIntent.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [ProposedIntent.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(ProposedIntent, page)}
end
-@spec create(params()) :: {:ok, ProposedIntent.t()} | {:error, chgset()}
+@spec all!(Page.t()) :: [ProposedIntent.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
+end
+
+@spec create(Schema.params()) :: {:ok, ProposedIntent.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
+ Multi.new()
+ |> multi_insert(params)
+ |> Repo.transaction()
+ |> case do
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
+ end
+end
+
+@spec create!(Schema.params()) :: ProposedIntent.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, ProposedIntent.t()} | {:error, String.t() | Changeset.t()}
+def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, ProposedIntent.chgset(params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: pi}} -> {:ok, pi}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id())
- :: {:ok, ProposedIntent.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: ProposedIntent.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id()) :: {:ok, ProposedIntent.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, & &1.one)
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: pi}} -> {:ok, pi}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+@spec delete!(Schema.id()) :: ProposedIntent.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
@spec preload(ProposedIntent.t(), :published_in | :publishes)
:: ProposedIntent.t()
-def preload(prop_int, :published_in) do
- Repo.preload(prop_int, :published_in)
+def preload(prop_int, x) when x in ~w[published_in publishes]a do
+ Repo.preload(prop_int, x)
+end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :proposed_intent
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, ProposedIntent.changeset(params))
+end
+
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &ProposedIntent.changeset(Map.fetch!(&1, "#{key}.one"), params))
end
-def preload(prop_int, :publishes) do
- Repo.preload(prop_int, :publishes)
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
end
end
diff --git a/src/zenflows/vf/proposed_intent/resolv.ex b/src/zenflows/vf/proposed_intent/resolv.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.ProposedIntent.Resolv do
-@moduledoc "Resolvers of ProposedIntent."
+@moduledoc false
alias Zenflows.VF.ProposedIntent.Domain
diff --git a/src/zenflows/vf/proposed_intent/type.ex b/src/zenflows/vf/proposed_intent/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.ProposedIntent.Type do
-@moduledoc "GraphQL types of ProposedIntent."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/proposed_to.ex b/src/zenflows/vf/proposed_to.ex
@@ -23,6 +23,8 @@ published to many agents.
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.Schema
alias Zenflows.VF.{Agent, Proposal}
@type t() :: %__MODULE__{
@@ -40,8 +42,8 @@ end
@cast @reqr
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/recipe_exchange.ex b/src/zenflows/vf/recipe_exchange.ex
@@ -22,7 +22,8 @@ Specifies an exchange agreement as part of a recipe.
use Zenflows.DB.Schema
-alias Zenflows.VF.Validate
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
@type t() :: %__MODULE__{
name: String.t(),
@@ -39,8 +40,8 @@ end
@cast @reqr ++ [:note]
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/recipe_exchange/domain.ex b/src/zenflows/vf/recipe_exchange/domain.ex
@@ -18,16 +18,11 @@
defmodule Zenflows.VF.RecipeExchange.Domain do
@moduledoc "Domain logic of RecipeExchanges."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.RecipeExchange
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, RecipeExchange.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -38,47 +33,106 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(RecipeExchange, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
+ :: RecipeExchange.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [RecipeExchange.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(RecipeExchange, page)}
+end
+
+@spec all!(Page.t()) :: [RecipeExchange.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
end
-@spec create(params()) :: {:ok, RecipeExchange.t()} | {:error, chgset()}
+@spec create(Schema.params())
+ :: {:ok, RecipeExchange.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, RecipeExchange.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: re}} -> {:ok, re}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params())
- :: {:ok, RecipeExchange.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: RecipeExchange.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, RecipeExchange.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &RecipeExchange.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: re}} -> {:ok, re}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id())
- :: {:ok, RecipeExchange.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: RecipeExchange.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id())
+ :: {:ok, RecipeExchange.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, & &1.one)
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: re}} -> {:ok, re}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+
+@spec delete!(Schema.id()) :: RecipeExchange.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :recipe_exchange
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, RecipeExchange.changeset(params))
+end
+
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &RecipeExchange.changeset(Map.fetch!(&1, "#{key}.one"), params))
+end
+
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
+end
end
diff --git a/src/zenflows/vf/recipe_exchange/resolv.ex b/src/zenflows/vf/recipe_exchange/resolv.ex
@@ -16,8 +16,9 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.RecipeExchange.Resolv do
-@moduledoc "Resolvers of RecipeExchanges."
+@moduledoc false
+alias Zenflows.GQL.Connection
alias Zenflows.VF.RecipeExchange.Domain
def recipe_exchange(params, _) do
@@ -25,7 +26,10 @@ def recipe_exchange(params, _) do
end
def recipe_exchanges(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_recipe_exchange(%{recipe_exchange: params}, _) do
diff --git a/src/zenflows/vf/recipe_exchange/type.ex b/src/zenflows/vf/recipe_exchange/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.RecipeExchange.Type do
-@moduledoc "GraphQL types of RecipeExchanges."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/recipe_flow.ex b/src/zenflows/vf/recipe_flow.ex
@@ -22,6 +22,8 @@ The specification of a resource inflow to, or outflow from, a recipe process.
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.VF.{
Action,
Measure,
@@ -29,7 +31,6 @@ alias Zenflows.VF.{
RecipeProcess,
RecipeResource,
Unit,
- Validate,
}
@type t() :: %__MODULE__{
@@ -68,38 +69,22 @@ end
]a
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
|> Validate.note(:note)
|> Measure.cast(:effort_quantity)
|> Measure.cast(:resource_quantity)
- |> check_measures()
+ |> Validate.exist_or([
+ :resource_quantity_has_unit_id, :effort_quantity_has_unit_id,
+ ], method: :both)
+ |> Validate.exist_or([
+ :resource_quantity_has_numerical_value, :effort_quantity_has_numerical_value
+ ], method: :both)
|> Changeset.assoc_constraint(:recipe_input_of)
|> Changeset.assoc_constraint(:recipe_output_of)
|> Changeset.assoc_constraint(:recipe_flow_resource)
end
-
-# Validates that the DB doesn't end up with null measures, meaning either
-# one must be filled.
-@spec check_measures(Changeset.t()) :: Changeset.t()
-defp check_measures(cset) do
- # `*_has_numerical_value` fields not nil when `*_has_unit_id`
- # fields are not nil. This is guranteed by the `Measure.cast/2`
- # functions.
- resqty = Changeset.get_field(cset, :resource_quantity_has_unit_id)
- effqty = Changeset.get_field(cset, :effort_quantity_has_unit_id)
-
- if resqty || effqty do
- cset
- else
- msg = "resource quantity and effort quantity cannot be null at the same time"
-
- cset
- |> Changeset.add_error(:resource_quantity, msg)
- |> Changeset.add_error(:effort_quantity, msg)
- end
-end
end
diff --git a/src/zenflows/vf/recipe_flow/domain.ex b/src/zenflows/vf/recipe_flow/domain.ex
@@ -18,20 +18,11 @@
defmodule Zenflows.VF.RecipeFlow.Domain do
@moduledoc "Domain logic of RecipeFlows."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
-alias Zenflows.VF.{
- Action,
- Measure,
- RecipeFlow,
-}
-
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
+alias Zenflows.VF.{Action, Measure, RecipeFlow}
+
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, RecipeFlow.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -42,78 +33,117 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(RecipeFlow, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: RecipeFlow.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [RecipeFlow.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(RecipeFlow, page)}
+end
+
+@spec all!(Page.t()) :: [RecipeFlow.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
end
-@spec create(params()) :: {:ok, RecipeFlow.t()} | {:error, chgset()}
+@spec create(Schema.params()) :: {:ok, RecipeFlow.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, RecipeFlow.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: rf}} -> {:ok, rf}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params())
- :: {:ok, RecipeFlow.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: RecipeFlow.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, RecipeFlow.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &RecipeFlow.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: rf}} -> {:ok, rf}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id()) :: {:ok, RecipeFlow.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: RecipeFlow.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id())
+ :: {:ok, RecipeFlow.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, & &1.one)
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: rf}} -> {:ok, rf}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+@spec delete!(Schema.id()) :: RecipeFlow.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
@spec preload(RecipeFlow.t(), :resource_quantity | :effort_quantity
| :recipe_flow_resource | :action | :recipe_input_of | :recipe_output_of
| :recipe_clause_of)
:: RecipeFlow.t()
-def preload(rec_flow, :resource_quantity) do
- Measure.preload(rec_flow, :resource_quantity)
-end
+def preload(rec_flow, x) when x in ~w[
+ recipe_flow_resource recipe_input_of recipe_output_of recipe_clause_of
+]a,
+ do: Repo.preload(rec_flow, x)
+def preload(rec_flow, :action),
+ do: Action.preload(rec_flow, :action)
+def preload(rec_flow, x) when x in ~w[resource_quantity effort_quantity]a,
+ do: Measure.preload(rec_flow, x)
-def preload(rec_flow, :effort_quantity) do
- Measure.preload(rec_flow, :effort_quantity)
-end
-
-def preload(rec_flow, :recipe_flow_resource) do
- Repo.preload(rec_flow, :recipe_flow_resource)
-end
+@spec multi_key() :: atom()
+def multi_key(), do: :recipe_flow
-def preload(rec_flow, :action) do
- Action.preload(rec_flow, :action)
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
end
-def preload(rec_flow, :recipe_input_of) do
- Repo.preload(rec_flow, :recipe_input_of)
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, RecipeFlow.changeset(params))
end
-def preload(rec_flow, :recipe_output_of) do
- Repo.preload(rec_flow, :recipe_output_of)
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &RecipeFlow.changeset(Map.fetch!(&1, "#{key}.one"), params))
end
-def preload(rec_flow, :recipe_clause_of) do
- Repo.preload(rec_flow, :recipe_clause_of)
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
end
end
diff --git a/src/zenflows/vf/recipe_flow/resolv.ex b/src/zenflows/vf/recipe_flow/resolv.ex
@@ -16,10 +16,11 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.RecipeFlow.Resolv do
-@moduledoc "Resolvers of RecipeFlow."
+@moduledoc false
use Absinthe.Schema.Notation
+alias Zenflows.GQL.Connection
alias Zenflows.VF.RecipeFlow.Domain
def recipe_flow(params, _) do
@@ -27,7 +28,10 @@ def recipe_flow(params, _) do
end
def recipe_flows(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_recipe_flow(%{recipe_flow: params}, _) do
diff --git a/src/zenflows/vf/recipe_flow/type.ex b/src/zenflows/vf/recipe_flow/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.RecipeFlow.Type do
-@moduledoc "GraphQL types of RecipeFlows."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/recipe_process.ex b/src/zenflows/vf/recipe_process.ex
@@ -22,11 +22,12 @@ Specifies a process in a recipe for use in planning from recipe.
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.VF.{
Duration,
ProcessSpecification,
TimeUnitEnum,
- Validate,
}
@type t() :: %__MODULE__{
@@ -54,8 +55,8 @@ end
@cast @reqr ++ ~w[process_classified_as note has_duration]a
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/recipe_process/domain.ex b/src/zenflows/vf/recipe_process/domain.ex
@@ -16,18 +16,13 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.RecipeProcess.Domain do
-@moduledoc "Domain logic of RecipeProcesss."
+@moduledoc "Domain logic of RecipeProcesses."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.{Duration, RecipeProcess}
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, RecipeProcess.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -38,57 +33,113 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(RecipeProcess, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
+ :: RecipeProcess.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
end
-@spec create(params()) :: {:ok, RecipeProcess.t()} | {:error, chgset()}
+@spec all(Page.t()) :: {:ok, [RecipeProcess.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(RecipeProcess, page)}
+end
+
+@spec all!(Page.t()) :: [RecipeProcess.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
+end
+
+@spec create(Schema.params())
+ :: {:ok, RecipeProcess.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, RecipeProcess.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: rp}} -> {:ok, rp}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params())
- :: {:ok, RecipeProcess.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: RecipeProcess.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, RecipeProcess.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &RecipeProcess.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: rp}} -> {:ok, rp}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id())
- :: {:ok, RecipeProcess.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: RecipeProcess.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id())
+ :: {:ok, RecipeProcess.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, & &1.one)
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: rp}} -> {:ok, rp}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+@spec delete!(Schema.id()) :: RecipeProcess.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
@spec preload(RecipeProcess.t(), :has_duration | :process_conforms_to)
:: RecipeProcess.t()
-def preload(rec_proc, :has_duration) do
- Duration.preload(rec_proc, :has_duration)
+def preload(rec_proc, :has_duration),
+ do: Duration.preload(rec_proc, :has_duration)
+def preload(rec_proc, :process_conforms_to),
+ do: Repo.preload(rec_proc, :process_conforms_to)
+
+@spec multi_key() :: atom()
+def multi_key(), do: :recipe_process
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, RecipeProcess.changeset(params))
+end
+
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &RecipeProcess.changeset(Map.fetch!(&1, "#{key}.one"), params))
end
-def preload(rec_proc, :process_conforms_to) do
- Repo.preload(rec_proc, :process_conforms_to)
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
end
end
diff --git a/src/zenflows/vf/recipe_process/resolv.ex b/src/zenflows/vf/recipe_process/resolv.ex
@@ -16,10 +16,11 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.RecipeProcess.Resolv do
-@moduledoc "Resolvers of RecipeProcess."
+@moduledoc false
use Absinthe.Schema.Notation
+alias Zenflows.GQL.Connection
alias Zenflows.VF.RecipeProcess.Domain
def recipe_process(params, _) do
@@ -27,7 +28,10 @@ def recipe_process(params, _) do
end
def recipe_processes(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_recipe_process(%{recipe_process: params}, _) do
diff --git a/src/zenflows/vf/recipe_process/type.ex b/src/zenflows/vf/recipe_process/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.RecipeProcess.Type do
-@moduledoc "GraphQL types of RecipeProcesses."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/recipe_resource.ex b/src/zenflows/vf/recipe_resource.ex
@@ -23,12 +23,10 @@ recipe.
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.File
-alias Zenflows.VF.{
- ResourceSpecification,
- Unit,
- Validate,
-}
+alias Zenflows.VF.{ResourceSpecification, Unit}
@type t() :: %__MODULE__{
name: String.t(),
@@ -60,14 +58,14 @@ end
]a
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
|> Validate.name(:name)
|> Validate.note(:note)
- |> Changeset.cast_assoc(:images, with: &File.chgset/2)
+ |> Changeset.cast_assoc(:images)
|> Validate.class(:resource_conforms_to)
|> Changeset.assoc_constraint(:unit_of_resource)
|> Changeset.assoc_constraint(:unit_of_effort)
diff --git a/src/zenflows/vf/recipe_resource/domain.ex b/src/zenflows/vf/recipe_resource/domain.ex
@@ -18,16 +18,11 @@
defmodule Zenflows.VF.RecipeResource.Domain do
@moduledoc "Domain logic of RecipeResources."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.RecipeResource
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, RecipeResource.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -38,61 +33,111 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(RecipeResource, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: RecipeResource.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [RecipeResource.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(RecipeResource, page)}
+end
+
+@spec all!(Page.t()) :: [RecipeResource.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
end
-@spec create(params()) :: {:ok, RecipeResource.t()} | {:error, chgset()}
+@spec create(Schema.params()) :: {:ok, RecipeResource.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, RecipeResource.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: rr}} -> {:ok, rr}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params())
- :: {:ok, RecipeResource.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: RecipeResource.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, RecipeResource.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &RecipeResource.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: rr}} -> {:ok, rr}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id())
- :: {:ok, RecipeResource.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: RecipeResource.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id()) :: {:ok, RecipeResource.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, & &1.one)
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: rr}} -> {:ok, rr}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+@spec delete!(Schema.id()) :: RecipeResource.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
@spec preload(RecipeResource.t(), :unit_of_resource | :unit_of_effort | :resource_conforms_to)
:: RecipeResource.t()
-def preload(rec_res, :unit_of_resource) do
- Repo.preload(rec_res, :unit_of_resource)
+def preload(rec_res, x) when x in ~w[
+ unit_of_resource unit_of_effort resource_conforms_to
+]a do
+ Repo.preload(rec_res, x)
+end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :recipe_resource
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, RecipeResource.changeset(params))
end
-def preload(rec_res, :unit_of_effort) do
- Repo.preload(rec_res, :unit_of_effort)
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &RecipeResource.changeset(Map.fetch!(&1, "#{key}.one"), params))
end
-def preload(rec_res, :resource_conforms_to) do
- Repo.preload(rec_res, :resource_conforms_to)
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
end
end
diff --git a/src/zenflows/vf/recipe_resource/resolv.ex b/src/zenflows/vf/recipe_resource/resolv.ex
@@ -16,8 +16,9 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.RecipeResource.Resolv do
-@moduledoc "Resolvers of RecipeResources."
+@moduledoc false
+alias Zenflows.GQL.Connection
alias Zenflows.VF.RecipeResource.Domain
def recipe_resource(params, _) do
@@ -25,7 +26,10 @@ def recipe_resource(params, _) do
end
def recipe_resources(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_recipe_resource(%{recipe_resource: params}, _) do
diff --git a/src/zenflows/vf/recipe_resource/type.ex b/src/zenflows/vf/recipe_resource/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.RecipeResource.Type do
-@moduledoc "GraphQL types of RecipeResources."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/resource_specification.ex b/src/zenflows/vf/resource_specification.ex
@@ -24,8 +24,10 @@ classification when more information is needed, particularly for recipes.
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.File
-alias Zenflows.VF.{Unit, Validate}
+alias Zenflows.VF.Unit
@type t() :: %__MODULE__{
name: String.t(),
@@ -54,14 +56,14 @@ end
]a
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
|> Validate.name(:name)
|> Validate.note(:note)
- |> Changeset.cast_assoc(:images, with: &File.chgset/2)
+ |> Changeset.cast_assoc(:images)
|> Validate.class(:resource_classified_as)
|> Changeset.assoc_constraint(:default_unit_of_resource)
|> Changeset.assoc_constraint(:default_unit_of_effort)
diff --git a/src/zenflows/vf/resource_specification/domain.ex b/src/zenflows/vf/resource_specification/domain.ex
@@ -18,16 +18,11 @@
defmodule Zenflows.VF.ResourceSpecification.Domain do
@moduledoc "Domain logic of ResourceSpecifications."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.ResourceSpecification
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, ResourceSpecification.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -38,63 +33,115 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(ResourceSpecification, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
+ :: ResourceSpecification.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
end
-@spec create(repo(), params())
- :: {:ok, ResourceSpecification.t()} | {:error, chgset()}
-def create(repo \\ Repo, params) do
+@spec all(Page.t()) :: {:ok, [ResourceSpecification.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(ResourceSpecification, page)}
+end
+
+@spec all!(Page.t()) :: [ResourceSpecification.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
+end
+
+@spec create(Schema.params())
+ :: {:ok, ResourceSpecification.t()} | {:error, Changeset.t()}
+def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, ResourceSpecification.chgset(params))
- |> repo.transaction()
+ |> multi_insert(params)
+ |> Repo.transaction()
|> case do
- {:ok, %{insert: rs}} -> {:ok, rs}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params())
- :: {:ok, ResourceSpecification.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: ResourceSpecification.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, ResourceSpecification.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &ResourceSpecification.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: rs}} -> {:ok, rs}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id())
- :: {:ok, ResourceSpecification.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: ResourceSpecification.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id())
+ :: {:ok, ResourceSpecification.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, & &1.one)
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: rs}} -> {:ok, rs}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+@spec delete!(Schema.id()) :: ResourceSpecification.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
@spec preload(ResourceSpecification.t(),
:images | :default_unit_of_resource | :default_unit_of_effort)
:: ResourceSpecification.t()
-def preload(res_spec, :images) do
- Repo.preload(res_spec, :images)
+def preload(res_spec, x) when x in ~w[
+ images default_unit_of_resource default_unit_of_effort
+]a do
+ Repo.preload(res_spec, x)
+end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :resource_specification
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, ResourceSpecification.changeset(params))
end
-def preload(res_spec, :default_unit_of_resource) do
- Repo.preload(res_spec, :default_unit_of_resource)
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &ResourceSpecification.changeset(Map.fetch!(&1, "#{key}.one"), params))
end
-def preload(res_spec, :default_unit_of_effort) do
- Repo.preload(res_spec, :default_unit_of_effort)
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
end
end
diff --git a/src/zenflows/vf/resource_specification/resolv.ex b/src/zenflows/vf/resource_specification/resolv.ex
@@ -16,8 +16,9 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.ResourceSpecification.Resolv do
-@moduledoc "Resolvers of ResourceSpecifications."
+@moduledoc false
+alias Zenflows.GQL.Connection
alias Zenflows.VF.ResourceSpecification.Domain
def resource_specification(params, _) do
@@ -25,7 +26,10 @@ def resource_specification(params, _) do
end
def resource_specifications(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_resource_specification(%{resource_specification: params}, _) do
diff --git a/src/zenflows/vf/resource_specification/type.ex b/src/zenflows/vf/resource_specification/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.ResourceSpecification.Type do
-@moduledoc "GraphQL types of ResourceSpecifications."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/role_behavior.ex b/src/zenflows/vf/role_behavior.ex
@@ -22,7 +22,8 @@ The general shape or behavior grouping of an agent relationship role.
use Zenflows.DB.Schema
-alias Zenflows.VF.Validate
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
@type t() :: %__MODULE__{
name: String.t(),
@@ -39,8 +40,8 @@ end
@cast @reqr ++ [:note]
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/role_behavior/domain.ex b/src/zenflows/vf/role_behavior/domain.ex
@@ -18,16 +18,11 @@
defmodule Zenflows.VF.RoleBehavior.Domain do
@moduledoc "Domain logic of RoleBehaviors."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.RoleBehavior
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, RoleBehavior.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -38,46 +33,105 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(RoleBehavior, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: RoleBehavior.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [RoleBehavior.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(RoleBehavior, page)}
+end
+
+@spec all!(Page.t()) :: [RoleBehavior.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
end
-@spec create(params()) :: {:ok, RoleBehavior.t()} | {:error, chgset()}
+@spec create(Schema.params())
+ :: {:ok, RoleBehavior.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, RoleBehavior.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: rb}} -> {:ok, rb}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params())
- :: {:ok, RoleBehavior.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: RoleBehavior.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, RoleBehavior.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &RoleBehavior.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: rb}} -> {:ok, rb}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id()) :: {:ok, RoleBehavior.t()} | {:error, chgset()}
+@spec update!(Schema.id(), Schema.params()) :: RoleBehavior.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id())
+ :: {:ok, RoleBehavior.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, & &1.one)
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: rb}} -> {:ok, rb}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+
+@spec delete!(Schema.id()) :: RoleBehavior.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :role_behavior
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, RoleBehavior.changeset(params))
+end
+
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &RoleBehavior.changeset(Map.fetch!(&1, "#{key}.one"), params))
+end
+
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
+end
end
diff --git a/src/zenflows/vf/role_behavior/resolv.ex b/src/zenflows/vf/role_behavior/resolv.ex
@@ -16,8 +16,9 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.RoleBehavior.Resolv do
-@moduledoc "Resolvers of RoleBehaviors."
+@moduledoc false
+alias Zenflows.GQL.Connection
alias Zenflows.VF.RoleBehavior.Domain
def role_behavior(params, _info) do
@@ -25,7 +26,10 @@ def role_behavior(params, _info) do
end
def role_behaviors(params, _info) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_role_behavior(%{role_behavior: params}, _info) do
diff --git a/src/zenflows/vf/role_behavior/type.ex b/src/zenflows/vf/role_behavior/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.RoleBehavior.Type do
-@moduledoc "GraphQL types of RoleBehaviors."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/satisfaction.ex b/src/zenflows/vf/satisfaction.ex
@@ -23,12 +23,13 @@ or events that partially or full satisfy one or more intents.
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.VF.{
EventOrCommitment,
Intent,
Measure,
Unit,
- Validate,
}
@type t() :: %__MODULE__{
@@ -56,8 +57,8 @@ end
@cast @reqr ++ ~w[resource_quantity effort_quantity note]a
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/scenario.ex b/src/zenflows/vf/scenario.ex
@@ -23,10 +23,11 @@ used for budgeting, analysis, plan refinement, etc.
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.VF.{
Scenario,
ScenarioDefinition,
- Validate,
}
@type t() :: %__MODULE__{
@@ -57,8 +58,8 @@ end
]a
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/scenario/domain.ex b/src/zenflows/vf/scenario/domain.ex
@@ -18,16 +18,11 @@
defmodule Zenflows.VF.Scenario.Domain do
@moduledoc "Domain logic of Scenarios."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.Scenario
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, Scenario.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -38,55 +33,109 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(Scenario, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: Scenario.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [Scenario.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(Scenario, page)}
+end
+
+@spec all!(Page.t()) :: [Scenario.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
end
-@spec create(params()) :: {:ok, Scenario.t()} | {:error, chgset()}
+@spec create(Schema.params()) :: {:ok, Scenario.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, Scenario.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: s}} -> {:ok, s}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params())
- :: {:ok, Scenario.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: Scenario.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, Scenario.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &Scenario.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: s}} -> {:ok, s}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id()) :: {:ok, Scenario.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: Scenario.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id())
+ :: {:ok, Scenario.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, & &1.one)
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: s}} -> {:ok, s}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+@spec delete!(Schema.id()) :: Scenario.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
@spec preload(Scenario.t(), :defined_as | :refinement_of) :: Scenario.t()
-def preload(scen, :defined_as) do
- Repo.preload(scen, :defined_as)
+def preload(scen, x) when x in ~w[defined_as refinement_of]a do
+ Repo.preload(scen, x)
+end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :scenario
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, Scenario.changeset(params))
+end
+
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &Scenario.changeset(Map.fetch!(&1, "#{key}.one"), params))
end
-def preload(scen, :refinement_of) do
- Repo.preload(scen, :refinement_of)
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
end
end
diff --git a/src/zenflows/vf/scenario/resolv.ex b/src/zenflows/vf/scenario/resolv.ex
@@ -16,10 +16,11 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Scenario.Resolv do
-@moduledoc "Resolvers of Scenario."
+@moduledoc false
use Absinthe.Schema.Notation
+alias Zenflows.GQL.Connection
alias Zenflows.VF.Scenario.Domain
def scenario(params, _) do
@@ -27,7 +28,10 @@ def scenario(params, _) do
end
def scenarios(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_scenario(%{scenario: params}, _) do
diff --git a/src/zenflows/vf/scenario/type.ex b/src/zenflows/vf/scenario/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Scenario.Type do
-@moduledoc "GraphQL types of Scenarios."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/scenario_definition.ex b/src/zenflows/vf/scenario_definition.ex
@@ -22,10 +22,11 @@ The type definition of one or more scenarios, such as Yearly Budget.
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.VF.{
Duration,
TimeUnitEnum,
- Validate,
}
@type t() :: %__MODULE__{
@@ -49,8 +50,8 @@ end
@cast @reqr ++ ~w[note has_duration]a
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
Changeset.cast(schema, params, @cast)
|> Changeset.validate_required(@reqr)
|> Validate.name(:name)
diff --git a/src/zenflows/vf/scenario_definition/domain.ex b/src/zenflows/vf/scenario_definition/domain.ex
@@ -18,16 +18,11 @@
defmodule Zenflows.VF.ScenarioDefinition.Domain do
@moduledoc "Domain logic of ScenarioDefinitions."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.{Duration, ScenarioDefinition}
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, ScenarioDefinition.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -38,52 +33,111 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(ScenarioDefinition, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
+ :: ScenarioDefinition.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [ScenarioDefinition.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(ScenarioDefinition, page)}
+end
+
+@spec all!(Page.t()) :: [ScenarioDefinition.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
end
-@spec create(params()) :: {:ok, ScenarioDefinition.t()} | {:error, chgset()}
+@spec create(Schema.params())
+ :: {:ok, ScenarioDefinition.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, ScenarioDefinition.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: sd}} -> {:ok, sd}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params())
- :: {:ok, ScenarioDefinition.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: ScenarioDefinition.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, ScenarioDefinition.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &ScenarioDefinition.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: sd}} -> {:ok, sd}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id())
- :: {:ok, ScenarioDefinition.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: ScenarioDefinition.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id())
+ :: {:ok, ScenarioDefinition.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, & &1.one)
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: sd}} -> {:ok, sd}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+@spec delete!(Schema.id()) :: ScenarioDefinition.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
@spec preload(ScenarioDefinition.t(), :has_duration) :: ScenarioDefinition.t()
def preload(scen_def, :has_duration) do
Duration.preload(scen_def, :has_duration)
end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :scenario_definition
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, ScenarioDefinition.changeset(params))
+end
+
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &ScenarioDefinition.changeset(Map.fetch!(&1, "#{key}.one"), params))
+end
+
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
+end
end
diff --git a/src/zenflows/vf/scenario_definition/resolv.ex b/src/zenflows/vf/scenario_definition/resolv.ex
@@ -16,10 +16,11 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.ScenarioDefinition.Resolv do
-@moduledoc "Resolvers of ScenarioDefinition."
+@moduledoc false
use Absinthe.Schema.Notation
+alias Zenflows.GQL.Connection
alias Zenflows.VF.ScenarioDefinition.Domain
def scenario_definition(params, _) do
@@ -27,7 +28,10 @@ def scenario_definition(params, _) do
end
def scenario_definitions(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_scenario_definition(%{scenario_definition: params}, _) do
diff --git a/src/zenflows/vf/scenario_definition/type.ex b/src/zenflows/vf/scenario_definition/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.ScenarioDefinition.Type do
-@moduledoc "GraphQL types of ScenarioDefinitiones."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/settlement.ex b/src/zenflows/vf/settlement.ex
@@ -23,12 +23,13 @@ that fully or partially settle one or more claims.
use Zenflows.DB.Schema
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
alias Zenflows.VF.{
Claim,
EconomicEvent,
Measure,
Unit,
- Validate,
}
@type t() :: %__MODULE__{
@@ -56,8 +57,8 @@ end
@cast @reqr ++ ~w[resource_quantity effort_quantity note]a
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/spatial_thing.ex b/src/zenflows/vf/spatial_thing.ex
@@ -22,7 +22,8 @@ A physical mappable location.
use Zenflows.DB.Schema
-alias Zenflows.VF.Validate
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
@type t() :: %__MODULE__{
id: String.t(),
@@ -48,8 +49,8 @@ schema "vf_spatial_thing" do
end
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/spatial_thing/domain.ex b/src/zenflows/vf/spatial_thing/domain.ex
@@ -19,16 +19,11 @@ defmodule Zenflows.VF.SpatialThing.Domain do
@moduledoc "Domain logic of SpatialThings."
# Basically, a fancy name for (geo)location. :P
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.SpatialThing
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, SpatialThing.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -39,46 +34,105 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(SpatialThing, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: SpatialThing.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [SpatialThing.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(SpatialThing, page)}
+end
+
+@spec all!(Page.t()) :: [SpatialThing.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
end
-@spec create(params()) :: {:ok, SpatialThing.t()} | {:error, chgset()}
+@spec create(Schema.params())
+ :: {:ok, SpatialThing.t()} | {:error, Changeset.t()}
def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, SpatialThing.chgset(params))
+ |> multi_insert(params)
|> Repo.transaction()
|> case do
- {:ok, %{insert: st}} -> {:ok, st}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params())
- :: {:ok, SpatialThing.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: SpatialThing.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, SpatialThing.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &SpatialThing.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: st}} -> {:ok, st}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id()) :: {:ok, SpatialThing.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: SpatialThing.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id())
+ :: {:ok, SpatialThing.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, & &1.one)
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: st}} -> {:ok, st}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+
+@spec delete!(Schema.id()) :: SpatialThing.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :spatial_thing
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, SpatialThing.changeset(params))
+end
+
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &SpatialThing.changeset(Map.fetch!(&1, "#{key}.one"), params))
+end
+
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
+end
end
diff --git a/src/zenflows/vf/spatial_thing/resolv.ex b/src/zenflows/vf/spatial_thing/resolv.ex
@@ -16,9 +16,10 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.SpatialThing.Resolv do
-@moduledoc "Resolvers of SpatialThings."
+@moduledoc false
# Basically, a fancy name for (geo)location. :P
+alias Zenflows.GQL.Connection
alias Zenflows.VF.SpatialThing.Domain
def spatial_thing(params, _) do
@@ -26,7 +27,10 @@ def spatial_thing(params, _) do
end
def spatial_things(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_spatial_thing(%{spatial_thing: params}, _) do
diff --git a/src/zenflows/vf/spatial_thing/type.ex b/src/zenflows/vf/spatial_thing/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.SpatialThing.Type do
-@moduledoc "GraphQL types of SpatialThings."
+@moduledoc false
# Basically, a fancy name for (geo)location. :P
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/time_unit/type.ex b/src/zenflows/vf/time_unit/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.TimeUnit.Type do
-@moduledoc "GraphQL types of TimeUnits."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/unit.ex b/src/zenflows/vf/unit.ex
@@ -23,7 +23,8 @@ From OM2 vocabulary.
use Zenflows.DB.Schema
-alias Zenflows.VF.Validate
+alias Ecto.Changeset
+alias Zenflows.DB.{Schema, Validate}
@type t() :: %__MODULE__{
id: String.t(),
@@ -41,8 +42,8 @@ end
@cast @reqr
@doc false
-@spec chgset(Schema.t(), params()) :: Changeset.t()
-def chgset(schema \\ %__MODULE__{}, params) do
+@spec changeset(Schema.t(), Schema.params()) :: Changeset.t()
+def changeset(schema \\ %__MODULE__{}, params) do
schema
|> Changeset.cast(params, @cast)
|> Changeset.validate_required(@reqr)
diff --git a/src/zenflows/vf/unit/domain.ex b/src/zenflows/vf/unit/domain.ex
@@ -18,16 +18,11 @@
defmodule Zenflows.VF.Unit.Domain do
@moduledoc "Domain logic of Units."
-alias Ecto.Multi
-alias Zenflows.DB.{Paging, Repo}
+alias Ecto.{Changeset, Multi}
+alias Zenflows.DB.{Page, Repo, Schema}
alias Zenflows.VF.Unit
-@typep repo() :: Ecto.Repo.t()
-@typep chgset() :: Ecto.Changeset.t()
-@typep id() :: Zenflows.DB.Schema.id()
-@typep params() :: Zenflows.DB.Schema.params()
-
-@spec one(repo(), id() | map() | Keyword.t())
+@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t())
:: {:ok, Unit.t()} | {:error, String.t()}
def one(repo \\ Repo, _)
def one(repo, id) when is_binary(id), do: one(repo, id: id)
@@ -38,47 +33,103 @@ def one(repo, clauses) do
end
end
-@spec all(Paging.params()) :: Paging.result()
-def all(params) do
- Paging.page(Unit, params)
+@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: Unit.t()
+def one!(repo \\ Repo, id_or_clauses) do
+ {:ok, value} = one(repo, id_or_clauses)
+ value
+end
+
+@spec all(Page.t()) :: {:ok, [Unit.t()]} | {:error, Changeset.t()}
+def all(page \\ Page.new()) do
+ {:ok, Page.all(Unit, page)}
end
-# `repo` is needed since we use that in a migration script.
-@spec create(repo(), params()) :: {:ok, Unit.t()} | {:error, chgset()}
-def create(repo \\ Repo, params) do
+@spec all!(Page.t()) :: [Unit.t()]
+def all!(page \\ Page.new()) do
+ {:ok, value} = all(page)
+ value
+end
+
+@spec create(Schema.params()) :: {:ok, Unit.t()} | {:error, Changeset.t()}
+def create(params) do
+ key = multi_key()
Multi.new()
- |> Multi.insert(:insert, Unit.chgset(params))
- |> repo.transaction()
+ |> multi_insert(params)
+ |> Repo.transaction()
|> case do
- {:ok, %{insert: u}} -> {:ok, u}
- {:error, _, cset, _} -> {:error, cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec update(id(), params())
- :: {:ok, Unit.t()} | {:error, String.t() | chgset()}
+@spec create!(Schema.params()) :: Unit.t()
+def create!(params) do
+ {:ok, value} = create(params)
+ value
+end
+
+@spec update(Schema.id(), Schema.params())
+ :: {:ok, Unit.t()} | {:error, String.t() | Changeset.t()}
def update(id, params) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.update(:update, &Unit.chgset(&1.one, params))
+ |> multi_update(id, params)
|> Repo.transaction()
|> case do
- {:ok, %{update: u}} -> {:ok, u}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
-@spec delete(id()) :: {:ok, Unit.t()} | {:error, String.t() | chgset()}
+@spec update!(Schema.id(), Schema.params()) :: Unit.t()
+def update!(id, params) do
+ {:ok, value} = update(id, params)
+ value
+end
+
+@spec delete(Schema.id()) :: {:ok, Unit.t()} | {:error, String.t() | Changeset.t()}
def delete(id) do
+ key = multi_key()
Multi.new()
- |> Multi.put(:id, id)
- |> Multi.run(:one, &one/2)
- |> Multi.delete(:delete, & &1.one)
+ |> multi_delete(id)
|> Repo.transaction()
|> case do
- {:ok, %{delete: u}} -> {:ok, u}
- {:error, _, msg_or_cset, _} -> {:error, msg_or_cset}
+ {:ok, %{^key => value}} -> {:ok, value}
+ {:error, _, reason, _} -> {:error, reason}
end
end
+
+@spec delete!(Schema.id()) :: Unit.t()
+def delete!(id) do
+ {:ok, value} = delete(id)
+ value
+end
+
+@spec multi_key() :: atom()
+def multi_key(), do: :unit
+
+@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_one(m, key \\ multi_key(), id) do
+ Multi.run(m, key, fn repo, _ -> one(repo, id) end)
+end
+
+@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t()
+def multi_insert(m, key \\ multi_key(), params) do
+ Multi.insert(m, key, Unit.changeset(params))
+end
+
+@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t()
+def multi_update(m, key \\ multi_key(), id, params) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.update(key,
+ &Unit.changeset(Map.fetch!(&1, "#{key}.one"), params))
+end
+
+@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t()
+def multi_delete(m, key \\ multi_key(), id) do
+ m
+ |> multi_one("#{key}.one", id)
+ |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one"))
+end
end
diff --git a/src/zenflows/vf/unit/resolv.ex b/src/zenflows/vf/unit/resolv.ex
@@ -16,8 +16,9 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Unit.Resolv do
-@moduledoc "Resolvers of Units."
+@moduledoc false
+alias Zenflows.GQL.Connection
alias Zenflows.VF.Unit.Domain
def unit(params, _) do
@@ -25,7 +26,10 @@ def unit(params, _) do
end
def units(params, _) do
- Domain.all(params)
+ with {:ok, page} <- Connection.parse(params),
+ {:ok, schemas} <- Domain.all(page) do
+ {:ok, Connection.from_list(schemas, page)}
+ end
end
def create_unit(%{unit: params}, _) do
diff --git a/src/zenflows/vf/unit/type.ex b/src/zenflows/vf/unit/type.ex
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Zenflows.VF.Unit.Type do
-@moduledoc "GraphQL types of Units."
+@moduledoc false
use Absinthe.Schema.Notation
diff --git a/src/zenflows/vf/validate.ex b/src/zenflows/vf/validate.ex
@@ -1,146 +0,0 @@
-# Zenflows is designed to implement the Valueflows vocabulary,
-# written and maintained by srfsh <info@dyne.org>.
-# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-defmodule Zenflows.VF.Validate do
-@moduledoc """
-Common Valueflows validators for Ecto.Changesets. All the limitations
-here are rough and can be changed in the future.
-"""
-
-alias Ecto.Changeset, as: Chset
-
-require Logger
-
-@doc "Checks if the given string field is [16, 2048] bytes long."
-@spec key(Chset.t(), atom()) :: Chset.t()
-def key(cset, field) do
- Chset.validate_change(cset, field, :valflow, fn
- _, str when byte_size(str) < 16 ->
- [{field, "should be at least 16 bytes long"}]
- _, str when byte_size(str) > 2048 ->
- [{field, "should be at most 2048 bytes long"}]
- _, _ ->
- []
- end)
-end
-
-@doc "Checks if the given string field is [1, 256] bytes long."
-@spec name(Chset.t(), atom()) :: Chset.t()
-def name(cset, field) do
- Chset.validate_change(cset, field, :valflow, fn
- _, str when byte_size(str) < 1 ->
- [{field, "should be at least 1 byte long"}]
- _, str when byte_size(str) > 256 ->
- [{field, "should be at most 256 bytes long"}]
- _, _ ->
- []
- end)
-end
-
-@doc "Checks if the given string field is [1, 2048] bytes long."
-@spec note(Chset.t(), atom()) :: Chset.t()
-def note(cset, field) do
- Chset.validate_change(cset, field, :valflow, fn
- _, str when byte_size(str) < 1 ->
- [{field, "should be at least 1 bytes long"}]
- _, str when byte_size(str) > 2048 ->
- [{field, "should be at most 2048 bytes long"}]
- _, _ ->
- []
- end)
-end
-
-@doc "Checks if the given string is [1, 512] bytes long."
-@spec uri(Chset.t(), atom()) :: Chset.t()
-def uri(cset, field) do
- Chset.validate_change(cset, field, :valflow, fn
- _, str when byte_size(str) < 1 ->
- [{field, "should be at least 1 bytes long"}]
- _, str when byte_size(str) > 512 ->
- [{field, "should be at most 512 bytes long"}]
- _, _ ->
- []
- end)
-end
-
-@mebibyte 1024 * 1024
-
-@doc """
-Check if the given base64-encoded binary data is at least 1B, at most
-25MiB in size. And, display a warning if it is longer than 4MiB.
-"""
-@spec img(Chset.t(), atom()) :: Chset.t()
-def img(cset, field) do
- Chset.validate_change(cset, field, :valflow, fn
- _, str when byte_size(str) < 1 ->
- [{field, "should be at least 1B long"}]
- _, str when byte_size(str) > 25 * @mebibyte ->
- [{field, "should be at most 25MiB long"}]
- _, str when byte_size(str) > 4 * @mebibyte ->
- Logger.warning("file exceeds 4MiB")
- []
- _, _ ->
- []
- end)
-end
-
-@doc """
-Checks if the given classifications (list of strings) for:
-
- - Each item in the list is [1, 512] bytes long;
- - The list can contain only [1, 128] items.
-"""
-@spec class(Chset.t(), atom()) :: Chset.t()
-def class(cset, field) do
- Chset.validate_change(cset, field, :valflow, fn
- _, [] ->
- [{field, "must contain at least 1 item"}]
- _, list ->
- case do_class(list) do
- {:exceeds, _ind} -> [{field, "must contain at most 128 items"}]
- {:short, ind} -> [{field, "the item at #{ind + 1} cannot be shorter than 1 bytes"}]
- {:long, ind} -> [{field, "the item at #{ind + 1} cannot be longer than 512 bytes"}]
- {:valid, _ind} -> []
- end
- end)
-end
-
-@spec do_class([String.t()]) :: {atom(), integer()}
-defp do_class(list) do
- do_class(list, 0, 128)
-end
-
-# The rationale of this function is to loop over the list while decreasing
-# `remaining' and increasing `index' until either one of these happen (in
-# that order):
-# * remaining hits 0
-# * one of the items in the list is shorter than 3 bytes long
-# * one of the items in the list is longer than 512 bytes long
-@spec do_class([String.t()], integer(), integer()) :: {atom(), integer()}
-defp do_class([head | tail], index, remaining) do
- cond do
- remaining == 0 -> {:exceeds, index}
- byte_size(head) < 1 -> {:short, index}
- byte_size(head) > 512 -> {:long, index}
- true -> do_class(tail, index + 1, remaining - 1)
- end
-end
-
-defp do_class([], index, _) do
- {:valid, index - 1}
-end
-end
diff --git a/test/db/page.test.exs b/test/db/page.test.exs
@@ -0,0 +1,298 @@
+# Zenflows is designed to implement the Valueflows vocabulary,
+# written and maintained by srfsh <info@dyne.org>.
+# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+defmodule ZenflowsTest.DB.Page do
+use ZenflowsTest.Help.EctoCase, async: true
+
+# What we are testing here is a bit interesting. Because, you see,
+# what we actually care about is dependant on the number of records we
+# ask for (referred to by "num" from now on). This is because of we
+# always try to fetch num+1 records. This basically means that we'll
+# have a table of possible cases:
+#
+# num | len(edges)
+# ----+-----------
+# 0 | 0
+# 0 | 1
+# ----+-----------
+# 1 | 0
+# 1 | 1
+# 1 | 2
+# ----+-----------
+# 2 | 0
+# 2 | 1
+# 2 | 2
+# 2 | 3
+# ----+-----------
+# 3 | 0
+# 3 | 1
+# 3 | 2
+# 3 | 3
+# 3 | 4
+# ----+-----------
+# 4 | 0
+# 4 | 1
+# 4 | 2
+# 4 | 3
+# 4 | 4
+# 4 | 5
+
+# Here, we cover the cases of:
+# num | len(edges)
+# ----+-----------
+# 0 | 0
+# 1 | 0
+# 2 | 0
+# 3 | 0
+# 4 | 0
+test "num>=0 && len(edges)==0:" do
+ Enum.each(0..10, fn n ->
+ assert %{data: %{"people" => data}} =
+ run!("""
+ query ($n: Int!) {
+ people (first: $n) {...people}
+ }
+ """, vars: %{"n" => n})
+
+ assert [] = Map.fetch!(data, "edges")
+
+ assert %{
+ "startCursor" => nil,
+ "endCursor" => nil,
+ "hasPreviousPage" => false,
+ "hasNextPage" => false,
+ "totalCount" => 0,
+ "pageLimit" => ^n,
+ } = Map.fetch!(data, "pageInfo")
+ end)
+end
+
+# Here, we cover the cases of:
+# num | len(edges)
+# ----+-----------
+# 1 | 1
+# 2 | 2
+# 3 | 3
+# 4 | 4
+test "num>=1 && len(edges)==num:" do
+ Enum.reduce(1..10, [], fn n, pers ->
+ last = %{id: last_cur} = Factory.insert!(:person)
+ pers = pers ++ [last]
+ [%{id: first_cur} | _] = pers
+
+ assert %{data: %{"people" => data}} =
+ run!("""
+ query ($n: Int!) {
+ people (first: $n) {...people}
+ }
+ """, vars: %{"n" => n})
+
+ edges = Map.fetch!(data, "edges")
+ assert length(edges) == n
+
+ assert %{
+ "startCursor" => ^first_cur,
+ "endCursor" => ^last_cur,
+ "hasPreviousPage" => false,
+ "hasNextPage" => false,
+ "totalCount" => ^n,
+ "pageLimit" => ^n,
+ } = Map.fetch!(data, "pageInfo")
+
+ pers
+ end)
+end
+
+# Here, we cover the cases of:
+# num | len(edges)
+# ----+-----------
+# 0 | 1
+# 1 | 2
+# 2 | 3
+# 3 | 4
+# 4 | 5
+test "num>=0 && len(edges)==num+1:" do
+ Enum.reduce(0..10, [], fn n, pers ->
+ pers = pers ++ [Factory.insert!(:person)]
+ {tmp, _} = Enum.split(pers, n)
+ first = List.first(tmp)
+ last = List.last(tmp)
+ first_cur = if first != nil, do: first.id, else: nil
+ last_cur = if last != nil, do: last.id, else: nil
+
+ assert %{data: %{"people" => data}} =
+ run!("""
+ query ($n: Int!) {
+ people (first: $n) {...people}
+ }
+ """, vars: %{"n" => n})
+
+ edges = Map.fetch!(data, "edges")
+ assert length(edges) == n
+
+ assert %{
+ "startCursor" => ^first_cur,
+ "endCursor" => ^last_cur,
+ "hasPreviousPage" => false,
+ "hasNextPage" => true,
+ "totalCount" => ^n,
+ "pageLimit" => ^n,
+ } = Map.fetch!(data, "pageInfo")
+
+ pers
+ end)
+end
+
+# Here, we cover the last case, which prooves we cover all the cases
+# (this is so because of the fact that we only deal with len(edges)<num
+# cases, where num>=1):
+# num | len(edges)
+# ----+-----------
+# 2 | 1
+# ----+-----------
+# 3 | 1
+# 3 | 2
+# ----+-----------
+# 4 | 1
+# 4 | 2
+# 4 | 3
+test "num>=2 && len(edges)>=0 && len(edges)<num:" do
+ Enum.reduce(1..9, [], fn e, pers ->
+ pers = pers ++ [Factory.insert!(:person)]
+
+ Enum.each(2..10, fn n ->
+ if e < n do
+ assert %{data: %{"people" => data}} =
+ run!("""
+ query ($n: Int!) {
+ people (first: $n) {...people}
+ }
+ """, vars: %{"n" => n})
+
+ edges = Map.fetch!(data, "edges")
+ assert length(edges) == e
+
+ %{id: first_cur} = List.first(pers)
+ %{id: last_cur} = List.last(pers)
+
+ assert %{
+ "startCursor" => ^first_cur,
+ "endCursor" => ^last_cur,
+ "hasPreviousPage" => false,
+ "hasNextPage" => false,
+ "totalCount" => ^e,
+ "pageLimit" => ^n,
+ } = Map.fetch!(data, "pageInfo")
+ end
+ end)
+
+ pers
+ end)
+end
+
+# We're dealing with cursors here now. Most of the cases are the
+# same as the ones without the cursors, so we omit them.
+
+# Here, we cover the cases of:
+# num | len(edges)
+# ----+-----------
+# 1 | 1
+# 2 | 2
+# 3 | 3
+# 4 | 4
+test "with cursor: num>=1 && len(edges)==num:" do
+ Enum.each(1..10, fn n ->
+ p = Factory.insert!(:person)
+
+ assert %{data: %{"people" => data}} =
+ run!("""
+ query ($cur: ID! $n: Int!) {
+ people (after: $cur first: $n) {...people}
+ }
+ """, vars: %{"n" => n, "cur" => p.id})
+
+ assert [] = Map.fetch!(data, "edges")
+
+ assert %{
+ "startCursor" => nil,
+ "endCursor" => nil,
+ "hasPreviousPage" => true, # spec says so if we can't determine
+ "hasNextPage" => false,
+ "totalCount" => 0,
+ "pageLimit" => ^n,
+ } = Map.fetch!(data, "pageInfo")
+ end)
+end
+
+# Here, we cover the cases of:
+# num | len(edges)
+# ----+-----------
+# 1 | 2
+# 2 | 3
+# 3 | 4
+# 4 | 5
+test "with cursor: num>=1 && len(edges)==num+1:" do
+ pers = [Factory.insert!(:person)]
+ Enum.reduce(1..10, pers, fn n, pers ->
+ %{id: after_cur} = List.last(pers)
+ last = %{id: last_cur} = Factory.insert!(:person)
+ pers = pers ++ [last]
+
+ assert %{data: %{"people" => data}} =
+ run!("""
+ query ($cur: ID! $n: Int!) {
+ people (after: $cur first: $n) {...people}
+ }
+ """, vars: %{"n" => n, "cur" => after_cur})
+
+ assert [_] = Map.fetch!(data, "edges")
+
+ assert %{
+ "startCursor" => ^last_cur,
+ "endCursor" => ^last_cur,
+ "hasPreviousPage" => true, # spec
+ "hasNextPage" => false,
+ "totalCount" => 1,
+ "pageLimit" => ^n,
+ } = Map.fetch!(data, "pageInfo")
+
+ pers
+ end)
+end
+
+@spec run!(String.t(), Keyword.t()) :: Absinthe.run_result()
+def run!(doc, opts \\ []) do
+ """
+ #{doc}
+ fragment people on PersonConnection {
+ pageInfo {
+ startCursor
+ endCursor
+ hasPreviousPage
+ hasNextPage
+ totalCount
+ pageLimit
+ }
+ edges {
+ cursor
+ node {id}
+ }
+ }
+ """
+ |> ZenflowsTest.Help.AbsinCase.run!(opts)
+end
+end
diff --git a/test/db/paging.test.exs b/test/db/paging.test.exs
@@ -1,298 +0,0 @@
-# Zenflows is designed to implement the Valueflows vocabulary,
-# written and maintained by srfsh <info@dyne.org>.
-# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-defmodule ZenflowsTest.DB.Paging do
-use ZenflowsTest.Help.EctoCase, async: true
-
-# What we are testing here is a bit interesting. Because, you see,
-# what we actually care about is dependant on the number of records we
-# ask for (referred to by "num" from now on). This is because of we
-# always try to fetch num+1 records. This basically means that we'll
-# have a table of possible cases:
-#
-# num | len(edges)
-# ----+-----------
-# 0 | 0
-# 0 | 1
-# ----+-----------
-# 1 | 0
-# 1 | 1
-# 1 | 2
-# ----+-----------
-# 2 | 0
-# 2 | 1
-# 2 | 2
-# 2 | 3
-# ----+-----------
-# 3 | 0
-# 3 | 1
-# 3 | 2
-# 3 | 3
-# 3 | 4
-# ----+-----------
-# 4 | 0
-# 4 | 1
-# 4 | 2
-# 4 | 3
-# 4 | 4
-# 4 | 5
-
-# Here, we cover the cases of:
-# num | len(edges)
-# ----+-----------
-# 0 | 0
-# 1 | 0
-# 2 | 0
-# 3 | 0
-# 4 | 0
-test "num>=0 && len(edges)==0:" do
- Enum.each(0..10, fn n ->
- assert %{data: %{"people" => data}} =
- run!("""
- query ($n: Int!) {
- people (first: $n) {...people}
- }
- """, vars: %{"n" => n})
-
- assert [] = Map.fetch!(data, "edges")
-
- assert %{
- "startCursor" => nil,
- "endCursor" => nil,
- "hasPreviousPage" => false,
- "hasNextPage" => false,
- "totalCount" => 0,
- "pageLimit" => ^n,
- } = Map.fetch!(data, "pageInfo")
- end)
-end
-
-# Here, we cover the cases of:
-# num | len(edges)
-# ----+-----------
-# 1 | 1
-# 2 | 2
-# 3 | 3
-# 4 | 4
-test "num>=1 && len(edges)==num:" do
- Enum.reduce(1..10, [], fn n, pers ->
- last = %{id: last_cur} = Factory.insert!(:person)
- pers = pers ++ [last]
- [%{id: first_cur} | _] = pers
-
- assert %{data: %{"people" => data}} =
- run!("""
- query ($n: Int!) {
- people (first: $n) {...people}
- }
- """, vars: %{"n" => n})
-
- edges = Map.fetch!(data, "edges")
- assert length(edges) == n
-
- assert %{
- "startCursor" => ^first_cur,
- "endCursor" => ^last_cur,
- "hasPreviousPage" => false,
- "hasNextPage" => false,
- "totalCount" => ^n,
- "pageLimit" => ^n,
- } = Map.fetch!(data, "pageInfo")
-
- pers
- end)
-end
-
-# Here, we cover the cases of:
-# num | len(edges)
-# ----+-----------
-# 0 | 1
-# 1 | 2
-# 2 | 3
-# 3 | 4
-# 4 | 5
-test "num>=0 && len(edges)==num+1:" do
- Enum.reduce(0..10, [], fn n, pers ->
- pers = pers ++ [Factory.insert!(:person)]
- {tmp, _} = Enum.split(pers, n)
- first = List.first(tmp)
- last = List.last(tmp)
- first_cur = if first != nil, do: first.id, else: nil
- last_cur = if last != nil, do: last.id, else: nil
-
- assert %{data: %{"people" => data}} =
- run!("""
- query ($n: Int!) {
- people (first: $n) {...people}
- }
- """, vars: %{"n" => n})
-
- edges = Map.fetch!(data, "edges")
- assert length(edges) == n
-
- assert %{
- "startCursor" => ^first_cur,
- "endCursor" => ^last_cur,
- "hasPreviousPage" => false,
- "hasNextPage" => true,
- "totalCount" => ^n,
- "pageLimit" => ^n,
- } = Map.fetch!(data, "pageInfo")
-
- pers
- end)
-end
-
-# Here, we cover the last case, which prooves we cover all the cases
-# (this is so because of the fact that we only deal with len(edges)<num
-# cases, where num>=1):
-# num | len(edges)
-# ----+-----------
-# 2 | 1
-# ----+-----------
-# 3 | 1
-# 3 | 2
-# ----+-----------
-# 4 | 1
-# 4 | 2
-# 4 | 3
-test "num>=2 && len(edges)>=0 && len(edges)<num:" do
- Enum.reduce(1..9, [], fn e, pers ->
- pers = pers ++ [Factory.insert!(:person)]
-
- Enum.each(2..10, fn n ->
- if e < n do
- assert %{data: %{"people" => data}} =
- run!("""
- query ($n: Int!) {
- people (first: $n) {...people}
- }
- """, vars: %{"n" => n})
-
- edges = Map.fetch!(data, "edges")
- assert length(edges) == e
-
- %{id: first_cur} = List.first(pers)
- %{id: last_cur} = List.last(pers)
-
- assert %{
- "startCursor" => ^first_cur,
- "endCursor" => ^last_cur,
- "hasPreviousPage" => false,
- "hasNextPage" => false,
- "totalCount" => ^e,
- "pageLimit" => ^n,
- } = Map.fetch!(data, "pageInfo")
- end
- end)
-
- pers
- end)
-end
-
-# We're dealing with cursors here now. Most of the cases are the
-# same as the ones without the cursors, so we omit them.
-
-# Here, we cover the cases of:
-# num | len(edges)
-# ----+-----------
-# 1 | 1
-# 2 | 2
-# 3 | 3
-# 4 | 4
-test "with cursor: num>=1 && len(edges)==num:" do
- Enum.each(1..10, fn n ->
- p = Factory.insert!(:person)
-
- assert %{data: %{"people" => data}} =
- run!("""
- query ($cur: ID! $n: Int!) {
- people (after: $cur first: $n) {...people}
- }
- """, vars: %{"n" => n, "cur" => p.id})
-
- assert [] = Map.fetch!(data, "edges")
-
- assert %{
- "startCursor" => nil,
- "endCursor" => nil,
- "hasPreviousPage" => true, # spec says so if we can't determine
- "hasNextPage" => false,
- "totalCount" => 0,
- "pageLimit" => ^n,
- } = Map.fetch!(data, "pageInfo")
- end)
-end
-
-# Here, we cover the cases of:
-# num | len(edges)
-# ----+-----------
-# 1 | 2
-# 2 | 3
-# 3 | 4
-# 4 | 5
-test "with cursor: num>=1 && len(edges)==num+1:" do
- pers = [Factory.insert!(:person)]
- Enum.reduce(1..10, pers, fn n, pers ->
- %{id: after_cur} = List.last(pers)
- last = %{id: last_cur} = Factory.insert!(:person)
- pers = pers ++ [last]
-
- assert %{data: %{"people" => data}} =
- run!("""
- query ($cur: ID! $n: Int!) {
- people (after: $cur first: $n) {...people}
- }
- """, vars: %{"n" => n, "cur" => after_cur})
-
- assert [_] = Map.fetch!(data, "edges")
-
- assert %{
- "startCursor" => ^last_cur,
- "endCursor" => ^last_cur,
- "hasPreviousPage" => true, # spec
- "hasNextPage" => false,
- "totalCount" => 1,
- "pageLimit" => ^n,
- } = Map.fetch!(data, "pageInfo")
-
- pers
- end)
-end
-
-@spec run!(String.t(), Keyword.t()) :: Absinthe.run_result()
-def run!(doc, opts \\ []) do
- """
- #{doc}
- fragment people on PersonConnection {
- pageInfo {
- startCursor
- endCursor
- hasPreviousPage
- hasNextPage
- totalCount
- pageLimit
- }
- edges {
- cursor
- node {id}
- }
- }
- """
- |> ZenflowsTest.Help.AbsinCase.run!(opts)
-end
-end
diff --git a/test/file.test.exs b/test/file.test.exs
@@ -67,7 +67,7 @@ test "works on EconomicResource" do
agent = Factory.insert!(:agent)
unit = Factory.insert!(:unit)
spec = Factory.insert!(:resource_specification)
- {:ok, %EconomicEvent{}, %EconomicResource{} = res, nil} =
+ {:ok, %EconomicEvent{} = evt} =
EconomicEvent.Domain.create(
%{
action_id: "raise",
@@ -108,6 +108,9 @@ test "works on EconomicResource" do
],
})
+ evt = EconomicEvent.Domain.preload(evt, :resource_inventoried_as)
+ res = EconomicResource.Domain.preload(evt.resource_inventoried_as, :images)
+
[%File{}, %File{}] = res.images
end
@@ -248,7 +251,7 @@ end
test "doesn't work without a belongs_to field" do
{:error, %Changeset{errors: errs}} =
- File.chgset(%File{}, %{
+ File.changeset(%File{}, %{
hash: "asnotehusnatoheusntaoehusntaeohu",
name: "satoehusnoaethu",
description: "foobaour",
diff --git a/test/vf/action.test.exs b/test/vf/action.test.exs
@@ -32,13 +32,13 @@ embedded_schema do
field :action, :map, virtual: true
end
-def chgset(params) do
+def changeset(params) do
%__MODULE__{}
|> common(params)
|> Map.put(:action, :insert)
end
-def chgset(schema, params) do
+def changeset(schema, params) do
schema
|> common(params)
|> Map.put(:action, :update)
@@ -56,20 +56,20 @@ end
test "insert" do
# doesn't work with invalid ids
assert %Changeset{valid?: false, changes: %{}, errors: errs}
- = Dummy.chgset(%{action_id: "doesn't exists"})
+ = Dummy.changeset(%{action_id: "doesn't exists"})
assert Keyword.has_key?(errs, :action_id)
# works with all valid ids
Enum.each(Action.ID.values(), fn x ->
assert %Changeset{valid?: true, changes: %{action_id: ^x}, errors: []}
- = Dummy.chgset(%{action_id: x})
+ = Dummy.changeset(%{action_id: x})
end)
end
test "update", %{inserted: schema} do
# doesn't work with invalid ids
assert %Changeset{valid?: false, changes: %{}, errors: errs}
- = Dummy.chgset(schema, %{action_id: "doesn't exists"})
+ = Dummy.changeset(schema, %{action_id: "doesn't exists"})
assert Keyword.has_key?(errs, :action_id)
# because if the values are the same, there won't be any change
@@ -77,7 +77,7 @@ test "update", %{inserted: schema} do
# works with all valid ids
Enum.each(all, fn x ->
assert %Changeset{valid?: true, changes: %{action_id: ^x}, errors: []}
- = Dummy.chgset(schema, %{action_id: x})
+ = Dummy.changeset(schema, %{action_id: x})
end)
end
diff --git a/test/vf/appreciation.test.exs b/test/vf/appreciation.test.exs
@@ -32,7 +32,7 @@ end
test "create Appreciation", %{params: params} do
assert {:ok, %Appreciation{} = appr} =
params
- |> Appreciation.chgset()
+ |> Appreciation.changeset()
|> Repo.insert()
assert appr.appreciation_of_id == params.appreciation_of_id
@@ -45,7 +45,7 @@ test "update Appreciation", %{params: params} do
assert {:ok, %Appreciation{} = appr} =
:appreciation
|> Factory.insert!()
- |> Appreciation.chgset(params)
+ |> Appreciation.changeset(params)
|> Repo.update()
assert appr.appreciation_of_id == params.appreciation_of_id
diff --git a/test/vf/claim.test.exs b/test/vf/claim.test.exs
@@ -48,7 +48,7 @@ end
test "create Claim", %{params: params} do
assert {:ok, %Claim{} = claim} =
params
- |> Claim.chgset()
+ |> Claim.changeset()
|> Repo.insert()
assert claim.action_id == params.action_id
@@ -73,7 +73,7 @@ test "update Appreciation", %{params: params} do
assert {:ok, %Claim{} = claim} =
:claim
|> Factory.insert!()
- |> Claim.chgset(params)
+ |> Claim.changeset(params)
|> Repo.update()
assert claim.action_id == params.action_id
diff --git a/test/vf/commitment.test.exs b/test/vf/commitment.test.exs
@@ -37,7 +37,12 @@ setup do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- due: DateTime.utc_now(),
+ resource_inventoried_as_id: Factory.insert!(:economic_resource).id,
+ resource_conforms_to_id: Factory.insert!(:resource_specification).id,
+ has_point_in_time: Factory.now(),
+ has_beginning: Factory.now(),
+ has_end: Factory.now(),
+ due: Factory.now(),
finished: Factory.bool(),
note: Factory.str("note"),
# in_scope_of_id:
@@ -51,12 +56,12 @@ end
describe "create Commitment" do
test "with both :has_point_in_time and :has_beginning", %{params: params} do
params = params
- |> Map.put(:has_point_in_time, DateTime.utc_now())
- |> Map.put(:has_beginning, DateTime.utc_now())
+ |> Map.delete(:resource_inventoried_as_id)
+ |> Map.delete(:has_end)
assert {:error, %Changeset{errors: errs}} =
params
- |> Commitment.chgset()
+ |> Commitment.changeset()
|> Repo.insert()
assert {:ok, _} = Keyword.fetch(errs, :has_point_in_time)
@@ -65,12 +70,12 @@ describe "create Commitment" do
test "with both :has_point_in_time and :has_end", %{params: params} do
params = params
- |> Map.put(:has_point_in_time, DateTime.utc_now())
- |> Map.put(:has_end, DateTime.utc_now())
+ |> Map.delete(:resource_inventoried_as_id)
+ |> Map.delete(:has_beginning)
assert {:error, %Changeset{errors: errs}} =
params
- |> Commitment.chgset()
+ |> Commitment.changeset()
|> Repo.insert()
assert {:ok, _} = Keyword.fetch(errs, :has_point_in_time)
@@ -78,11 +83,14 @@ describe "create Commitment" do
end
test "with only :has_point_in_time", %{params: params} do
- params = Map.put(params, :has_point_in_time, DateTime.utc_now())
+ params = params
+ |> Map.delete(:resource_inventoried_as_id)
+ |> Map.delete(:has_beginning)
+ |> Map.delete(:has_end)
assert {:ok, %Commitment{} = comm} =
params
- |> Commitment.chgset()
+ |> Commitment.changeset()
|> Repo.insert()
assert comm.action_id == params.action_id
@@ -91,7 +99,7 @@ describe "create Commitment" do
assert comm.input_of_id == params.input_of_id
assert comm.output_of_id == params.output_of_id
assert comm.resource_classified_as == params.resource_classified_as
- assert comm.resource_conforms_to_id == nil
+ assert comm.resource_conforms_to_id == params.resource_conforms_to_id
assert comm.resource_inventoried_as_id == nil
assert comm.resource_quantity_has_unit_id == params.resource_quantity.has_unit_id
assert comm.resource_quantity_has_numerical_value == params.resource_quantity.has_numerical_value
@@ -111,11 +119,14 @@ describe "create Commitment" do
end
test "with only :has_beginning", %{params: params} do
- params = Map.put(params, :has_beginning, DateTime.utc_now())
+ params = params
+ |> Map.delete(:resource_inventoried_as_id)
+ |> Map.delete(:has_point_in_time)
+ |> Map.delete(:has_end)
assert {:ok, %Commitment{} = comm} =
params
- |> Commitment.chgset()
+ |> Commitment.changeset()
|> Repo.insert()
assert comm.action_id == params.action_id
@@ -124,7 +135,7 @@ describe "create Commitment" do
assert comm.input_of_id == params.input_of_id
assert comm.output_of_id == params.output_of_id
assert comm.resource_classified_as == params.resource_classified_as
- assert comm.resource_conforms_to_id == nil
+ assert comm.resource_conforms_to_id == params.resource_conforms_to_id
assert comm.resource_inventoried_as_id == nil
assert comm.resource_quantity_has_unit_id == params.resource_quantity.has_unit_id
assert comm.resource_quantity_has_numerical_value == params.resource_quantity.has_numerical_value
@@ -144,11 +155,14 @@ describe "create Commitment" do
end
test "with only :has_end", %{params: params} do
- params = Map.put(params, :has_end, DateTime.utc_now())
+ params = params
+ |> Map.delete(:resource_inventoried_as_id)
+ |> Map.delete(:has_point_in_time)
+ |> Map.delete(:has_beginning)
assert {:ok, %Commitment{} = comm} =
params
- |> Commitment.chgset()
+ |> Commitment.changeset()
|> Repo.insert()
assert comm.action_id == params.action_id
@@ -157,8 +171,8 @@ describe "create Commitment" do
assert comm.input_of_id == params.input_of_id
assert comm.output_of_id == params.output_of_id
assert comm.resource_classified_as == params.resource_classified_as
- assert comm.resource_conforms_to_id == nil
- assert comm.resource_inventoried_as_id == nil
+ assert comm.resource_conforms_to_id == params.resource_conforms_to_id
+ assert comm.resource_inventoried_as_id == nil
assert comm.resource_quantity_has_unit_id == params.resource_quantity.has_unit_id
assert comm.resource_quantity_has_numerical_value == params.resource_quantity.has_numerical_value
assert comm.effort_quantity_has_unit_id == params.effort_quantity.has_unit_id
@@ -178,12 +192,12 @@ describe "create Commitment" do
test "with both :has_beginning and :has_end", %{params: params} do
params = params
- |> Map.put(:has_beginning, DateTime.utc_now())
- |> Map.put(:has_end, DateTime.utc_now())
+ |> Map.delete(:resource_inventoried_as_id)
+ |> Map.delete(:has_point_in_time)
assert {:ok, %Commitment{} = comm} =
params
- |> Commitment.chgset()
+ |> Commitment.changeset()
|> Repo.insert()
assert comm.action_id == params.action_id
@@ -192,7 +206,7 @@ describe "create Commitment" do
assert comm.input_of_id == params.input_of_id
assert comm.output_of_id == params.output_of_id
assert comm.resource_classified_as == params.resource_classified_as
- assert comm.resource_conforms_to_id == nil
+ assert comm.resource_conforms_to_id == params.resource_conforms_to_id
assert comm.resource_inventoried_as_id == nil
assert comm.resource_quantity_has_unit_id == params.resource_quantity.has_unit_id
assert comm.resource_quantity_has_numerical_value == params.resource_quantity.has_numerical_value
@@ -212,14 +226,11 @@ describe "create Commitment" do
end
test "with both :resource_conforms_to and :resource_inventoried_as", %{params: params} do
- params = params
- |> Map.put(:resource_conforms_to_id, Factory.insert!(:resource_specification).id)
- |> Map.put(:resource_inventoried_as_id, Factory.insert!(:economic_resource).id)
- |> Map.put(:has_point_in_time, DateTime.utc_now())
+ params = Map.delete(params, :has_point_in_time)
assert {:error, %Changeset{errors: errs}} =
params
- |> Commitment.chgset()
+ |> Commitment.changeset()
|> Repo.insert()
assert {:ok, _} = Keyword.fetch(errs, :resource_conforms_to_id)
@@ -228,12 +239,12 @@ describe "create Commitment" do
test "with only :resource_conforms_to", %{params: params} do
params = params
- |> Map.put(:resource_conforms_to_id, Factory.insert!(:resource_specification).id)
- |> Map.put(:has_point_in_time, DateTime.utc_now())
+ |> Map.delete(:has_point_in_time)
+ |> Map.delete(:resource_inventoried_as_id)
assert {:ok, %Commitment{} = comm} =
params
- |> Commitment.chgset()
+ |> Commitment.changeset()
|> Repo.insert()
assert comm.action_id == params.action_id
@@ -248,9 +259,9 @@ describe "create Commitment" do
assert comm.resource_quantity_has_numerical_value == params.resource_quantity.has_numerical_value
assert comm.effort_quantity_has_unit_id == params.effort_quantity.has_unit_id
assert comm.effort_quantity_has_numerical_value == params.effort_quantity.has_numerical_value
- assert comm.has_beginning == nil
- assert comm.has_end == nil
- assert comm.has_point_in_time == params.has_point_in_time
+ assert comm.has_beginning == params.has_beginning
+ assert comm.has_end == params.has_end
+ assert comm.has_point_in_time == nil
assert comm.due == params.due
assert comm.finished == params.finished
assert comm.note == params.note
@@ -263,12 +274,12 @@ describe "create Commitment" do
test "with only :resource_inventoried_as", %{params: params} do
params = params
- |> Map.put(:resource_inventoried_as_id, Factory.insert!(:economic_resource).id)
- |> Map.put(:has_point_in_time, DateTime.utc_now())
+ |> Map.delete(:has_point_in_time)
+ |> Map.delete(:resource_conforms_to_id)
assert {:ok, %Commitment{} = comm} =
params
- |> Commitment.chgset()
+ |> Commitment.changeset()
|> Repo.insert()
assert comm.action_id == params.action_id
@@ -283,9 +294,9 @@ describe "create Commitment" do
assert comm.resource_quantity_has_numerical_value == params.resource_quantity.has_numerical_value
assert comm.effort_quantity_has_unit_id == params.effort_quantity.has_unit_id
assert comm.effort_quantity_has_numerical_value == params.effort_quantity.has_numerical_value
- assert comm.has_beginning == nil
- assert comm.has_end == nil
- assert comm.has_point_in_time == params.has_point_in_time
+ assert comm.has_beginning == params.has_beginning
+ assert comm.has_end == params.has_end
+ assert comm.has_point_in_time == nil
assert comm.due == params.due
assert comm.finished == params.finished
assert comm.note == params.note
@@ -296,37 +307,4 @@ describe "create Commitment" do
assert comm.clause_of_id == params.clause_of_id
end
end
-
-test "with present assocs", %{params: params} do
- old = Factory.insert!(:commitment)
-
- assert {:ok, %Commitment{} = new} =
- old
- |> Commitment.chgset(params)
- |> Repo.update()
-
- assert new.action_id == params.action_id
- assert new.provider_id == params.provider_id
- assert new.receiver_id == params.receiver_id
- assert new.input_of_id == params.input_of_id
- assert new.output_of_id == params.output_of_id
- assert new.resource_classified_as == params.resource_classified_as
- assert new.resource_conforms_to_id == old.resource_conforms_to_id
- assert new.resource_inventoried_as_id == old.resource_inventoried_as_id
- assert new.resource_quantity_has_unit_id == params.resource_quantity.has_unit_id
- assert new.resource_quantity_has_numerical_value == params.resource_quantity.has_numerical_value
- assert new.effort_quantity_has_unit_id == params.effort_quantity.has_unit_id
- assert new.effort_quantity_has_numerical_value == params.effort_quantity.has_numerical_value
- assert new.has_beginning == old.has_beginning
- assert new.has_end == old.has_end
- assert new.has_point_in_time == old.has_point_in_time
- assert new.due == params.due
- assert new.finished == params.finished
- assert new.note == params.note
- # assert in_scope_of_id
- assert new.agreed_in == params.agreed_in
- assert new.independent_demand_of_id == params.independent_demand_of_id
- assert new.at_location_id == params.at_location_id
- assert new.clause_of_id == params.clause_of_id
-end
end
diff --git a/test/vf/duration.test.exs b/test/vf/duration.test.exs
@@ -32,13 +32,13 @@ embedded_schema do
field :has_duration_numeric_duration, :float
end
-def chgset(params) do
+def changeset(params) do
%__MODULE__{}
|> common(params)
|> Map.put(:action, :insert)
end
-def chgset(schema, params) do
+def changeset(schema, params) do
schema
|> common(params)
|> Map.put(:action, :update)
@@ -66,42 +66,42 @@ end
test "insert", %{params: params} do
# no changes when params is `%{}`
- assert %Changeset{valid?: true, changes: %{}} = Dummy.chgset(%{})
+ assert %Changeset{valid?: true, changes: %{}} = Dummy.changeset(%{})
# fields are nil when `:has_duration` is `nil`
- assert %Changeset{valid?: true, changes: chgs} = Dummy.chgset(%{has_duration: nil})
+ assert %Changeset{valid?: true, changes: chgs} = Dummy.changeset(%{has_duration: nil})
assert chgs.has_duration_unit_type == nil
assert chgs.has_duration_numeric_duration == nil
# fields are properly set when `:has_duration` is properly set
- assert %Changeset{valid?: true, changes: chgs} = Dummy.chgset(%{has_duration: params})
+ assert %Changeset{valid?: true, changes: chgs} = Dummy.changeset(%{has_duration: params})
assert chgs.has_duration_unit_type == params.unit_type
assert chgs.has_duration_numeric_duration == params.numeric_duration
# when no fields are provided, no fields are set
assert %Changeset{valid?: false, changes: chgs, errors: errs}
- = Dummy.chgset(%{has_duration: %{}})
+ = Dummy.changeset(%{has_duration: %{}})
assert length(Keyword.get_values(errs, :has_duration)) == 2
refute Map.has_key?(chgs, :has_duration_unit_type)
or Map.has_key?(chgs, :has_duration_numeric_duration)
# when `:unit_type` is `nil`, no fields are set
assert %Changeset{valid?: false, changes: chgs, errors: errs}
- = Dummy.chgset(%{has_duration: %{unit_type: nil}})
+ = Dummy.changeset(%{has_duration: %{unit_type: nil}})
assert length(Keyword.get_values(errs, :has_duration)) == 2
refute Map.has_key?(chgs, :has_duration_unit_type)
or Map.has_key?(chgs, :has_duration_numeric_duration)
# when `:numeric_duration` is `nil`, no fields are set
assert %Changeset{valid?: false, changes: %{has_duration: _}, errors: errs}
- = Dummy.chgset(%{has_duration: %{numeric_duration: nil}})
+ = Dummy.changeset(%{has_duration: %{numeric_duration: nil}})
assert length(Keyword.get_values(errs, :has_duration)) == 2
refute Map.has_key?(chgs, :has_duration_unit_type)
or Map.has_key?(chgs, :has_duration_numeric_duration)
# when both fields are `nil`, no fields are set
assert %Changeset{valid?: false, changes: %{has_duration: _}, errors: errs}
- = Dummy.chgset(%{has_duration: %{unit_type: nil, numeric_duration: nil}})
+ = Dummy.changeset(%{has_duration: %{unit_type: nil, numeric_duration: nil}})
assert length(Keyword.get_values(errs, :has_duration)) == 2
refute Map.has_key?(chgs, :has_duration_unit_type)
or Map.has_key?(chgs, :has_duration_numeric_duration)
@@ -109,17 +109,17 @@ end
test "update", %{params: params, inserted: schema} do
# no changes when params is `%{}`
- assert %Changeset{valid?: true, changes: %{}} = Dummy.chgset(schema, %{})
+ assert %Changeset{valid?: true, changes: %{}} = Dummy.changeset(schema, %{})
# fields are nil when `:has_duration` is `nil`
assert %Changeset{valid?: true, changes: %{
has_duration_unit_type: nil,
has_duration_numeric_duration: nil,
- }} = Dummy.chgset(schema, %{has_duration: nil})
+ }} = Dummy.changeset(schema, %{has_duration: nil})
# fields are changed when `:has_duration` is properly set
assert %Changeset{valid?: true, changes: chgs}
- = Dummy.chgset(schema, %{has_duration: params})
+ = Dummy.changeset(schema, %{has_duration: params})
# since ecto won't change it if it is already there
if schema.has_duration_unit_type != params.unit_type,
do: assert chgs.has_duration_unit_type == params.unit_type
@@ -127,28 +127,28 @@ test "update", %{params: params, inserted: schema} do
# when no fields are provided, no fields are set
assert %Changeset{valid?: false, changes: chgs, errors: errs}
- = Dummy.chgset(schema, %{has_duration: %{}})
+ = Dummy.changeset(schema, %{has_duration: %{}})
assert length(Keyword.get_values(errs, :has_duration)) == 2
refute Map.has_key?(chgs, :has_duration_unit_type)
or Map.has_key?(chgs, :has_duration_numeric_duration)
# when `:unit_type` is `nil`, no fields are set
assert %Changeset{valid?: false, changes: chgs, errors: errs}
- = Dummy.chgset(schema, %{has_duration: %{unit_type: nil}})
+ = Dummy.changeset(schema, %{has_duration: %{unit_type: nil}})
assert length(Keyword.get_values(errs, :has_duration)) == 2
refute Map.has_key?(chgs, :has_duration_unit_type)
or Map.has_key?(chgs, :has_duration_numeric_duration)
# when `:numeric_duration` is `nil`, no fields are set
assert %Changeset{valid?: false, changes: %{has_duration: _}, errors: errs}
- = Dummy.chgset(schema, %{has_duration: %{numeric_duration: nil}})
+ = Dummy.changeset(schema, %{has_duration: %{numeric_duration: nil}})
assert length(Keyword.get_values(errs, :has_duration)) == 2
refute Map.has_key?(chgs, :has_duration_unit_type)
or Map.has_key?(chgs, :has_duration_numeric_duration)
# when both fields are `nil`, no fields are set
assert %Changeset{valid?: false, changes: %{has_duration: _}, errors: errs}
- = Dummy.chgset(schema, %{has_duration: %{unit_type: nil, numeric_duration: nil}})
+ = Dummy.changeset(schema, %{has_duration: %{unit_type: nil, numeric_duration: nil}})
assert length(Keyword.get_values(errs, :has_duration)) == 2
refute Map.has_key?(chgs, :has_duration_unit_type)
or Map.has_key?(chgs, :has_duration_numeric_duration)
diff --git a/test/vf/economic_event.test.exs b/test/vf/economic_event.test.exs
@@ -21,12 +21,16 @@ use ZenflowsTest.Help.EctoCase, async: true
alias Ecto.Changeset
alias Zenflows.VF.EconomicEvent
+setup_all do
+ [errmsg_exist_xnor: "exactly one of them must be provided"]
+end
+
test """
`chgset/1`: every event requires the `:action_id`, `:provider_id`,
`:receiver_id` fields and the allowed combinations of the datetime
fields `:has_point_in_time`, `:has_beginning`, `:has_end`
""" do
- assert %Changeset{valid?: false} = cset = EconomicEvent.chgset(%{})
+ assert %Changeset{valid?: false} = cset = EconomicEvent.changeset(%{})
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:action_id])
assert {[_], err} = pop_in(err[:provider_id])
@@ -37,7 +41,7 @@ fields `:has_point_in_time`, `:has_beginning`, `:has_end`
assert err == %{}
assert %Changeset{valid?: false} = cset =
- EconomicEvent.chgset(%{has_point_in_time: DateTime.utc_now()})
+ EconomicEvent.changeset(%{has_point_in_time: DateTime.utc_now()})
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:action_id])
assert {[_], err} = pop_in(err[:provider_id])
@@ -45,7 +49,7 @@ fields `:has_point_in_time`, `:has_beginning`, `:has_end`
assert err == %{}
assert %Changeset{valid?: false} = cset =
- EconomicEvent.chgset(%{has_beginning: DateTime.utc_now()})
+ EconomicEvent.changeset(%{has_beginning: DateTime.utc_now()})
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:action_id])
assert {[_], err} = pop_in(err[:provider_id])
@@ -53,7 +57,7 @@ fields `:has_point_in_time`, `:has_beginning`, `:has_end`
assert err == %{}
assert %Changeset{valid?: false} = cset =
- EconomicEvent.chgset(%{has_end: DateTime.utc_now()})
+ EconomicEvent.changeset(%{has_end: DateTime.utc_now()})
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:action_id])
assert {[_], err} = pop_in(err[:provider_id])
@@ -61,7 +65,7 @@ fields `:has_point_in_time`, `:has_beginning`, `:has_end`
assert err == %{}
assert %Changeset{valid?: false} = cset =
- EconomicEvent.chgset(%{
+ EconomicEvent.changeset(%{
has_beginning: DateTime.utc_now(),
has_end: DateTime.utc_now(),
})
@@ -93,7 +97,7 @@ describe "`chgset/1` with raise:" do
assert %Changeset{valid?: true} =
params
|> Map.put(:resource_conforms_to_id, spec.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
end
test "pass with `:resource_inventoried_as`", %{params: params} do
@@ -101,30 +105,30 @@ describe "`chgset/1` with raise:" do
assert %Changeset{valid?: true} =
params
|> Map.put(:resource_inventoried_as_id, res.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
end
- test "fail without `:resource_conforms_to` and `:resource_inventoried_as`", %{params: params} do
- assert %Changeset{valid?: false} = cset = EconomicEvent.chgset(params)
+ test "fail without `:resource_conforms_to` and `:resource_inventoried_as`",
+ %{params: params, errmsg_exist_xnor: errmsg} do
+ assert %Changeset{valid?: false} = cset = EconomicEvent.changeset(params)
err = Changeset.traverse_errors(cset, &elem(&1, 0))
- msg = "these are mutually exclusive and exactly one must be provided"
- assert {[^msg], err} = pop_in(err[:resource_conforms_to_id])
- assert {[^msg], err} = pop_in(err[:resource_inventoried_as_id])
+ assert {[^errmsg], err} = pop_in(err[:resource_conforms_to_id])
+ assert {[^errmsg], err} = pop_in(err[:resource_inventoried_as_id])
assert err == %{}
end
- test "fail with `:resource_conforms_to` and `:resource_inventoried_as`", %{params: params} do
+ test "fail with `:resource_conforms_to` and `:resource_inventoried_as`",
+ %{params: params, errmsg_exist_xnor: errmsg} do
res = Factory.insert!(:economic_resource)
spec = Factory.insert!(:resource_specification)
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:resource_inventoried_as_id, res.id)
|> Map.put(:resource_conforms_to_id, spec.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
- msg = "these are mutually exclusive and exactly one must be provided"
- assert {[^msg], err} = pop_in(err[:resource_conforms_to_id])
- assert {[^msg], err} = pop_in(err[:resource_inventoried_as_id])
+ assert {[^errmsg], err} = pop_in(err[:resource_conforms_to_id])
+ assert {[^errmsg], err} = pop_in(err[:resource_inventoried_as_id])
assert err == %{}
end
@@ -136,7 +140,7 @@ describe "`chgset/1` with raise:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:provider_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -145,7 +149,7 @@ describe "`chgset/1` with raise:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:receiver_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -175,7 +179,7 @@ describe "`chgset/1` with produce:" do
assert %Changeset{valid?: true} =
params
|> Map.put(:resource_conforms_to_id, spec.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
end
test "pass with `:resource_inventoried_as`", %{params: params} do
@@ -183,30 +187,30 @@ describe "`chgset/1` with produce:" do
assert %Changeset{valid?: true} =
params
|> Map.put(:resource_inventoried_as_id, res.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
end
- test "fail without `:resource_conforms_to` and `:resource_inventoried_as`", %{params: params} do
- assert %Changeset{valid?: false} = cset = EconomicEvent.chgset(params)
+ test "fail without `:resource_conforms_to` and `:resource_inventoried_as`",
+ %{params: params, errmsg_exist_xnor: errmsg} do
+ assert %Changeset{valid?: false} = cset = EconomicEvent.changeset(params)
err = Changeset.traverse_errors(cset, &elem(&1, 0))
- msg = "these are mutually exclusive and exactly one must be provided"
- assert {[^msg], err} = pop_in(err[:resource_conforms_to_id])
- assert {[^msg], err} = pop_in(err[:resource_inventoried_as_id])
+ assert {[^errmsg], err} = pop_in(err[:resource_conforms_to_id])
+ assert {[^errmsg], err} = pop_in(err[:resource_inventoried_as_id])
assert err == %{}
end
- test "fail with `:resource_conforms_to` and `:resource_inventoried_as`", %{params: params} do
+ test "fail with `:resource_conforms_to` and `:resource_inventoried_as`",
+ %{params: params, errmsg_exist_xnor: errmsg} do
res = Factory.insert!(:economic_resource)
spec = Factory.insert!(:resource_specification)
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:resource_inventoried_as_id, res.id)
|> Map.put(:resource_conforms_to_id, spec.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
- msg = "these are mutually exclusive and exactly one must be provided"
- assert {[^msg], err} = pop_in(err[:resource_conforms_to_id])
- assert {[^msg], err} = pop_in(err[:resource_inventoried_as_id])
+ assert {[^errmsg], err} = pop_in(err[:resource_conforms_to_id])
+ assert {[^errmsg], err} = pop_in(err[:resource_inventoried_as_id])
assert err == %{}
end
@@ -218,7 +222,7 @@ describe "`chgset/1` with produce:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:provider_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -227,7 +231,7 @@ describe "`chgset/1` with produce:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:receiver_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -253,7 +257,7 @@ describe "`chgset/1` with lower:" do
end
test "pass when all good", %{params: params} do
- assert %Changeset{valid?: true} = EconomicEvent.chgset(params)
+ assert %Changeset{valid?: true} = EconomicEvent.changeset(params)
end
test "fail when `:provider` and `:receiver` differ", %{params: params} do
@@ -262,7 +266,7 @@ describe "`chgset/1` with lower:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:provider_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -271,7 +275,7 @@ describe "`chgset/1` with lower:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:receiver_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -299,7 +303,7 @@ describe "`chgset/1` with consume:" do
end
test "pass when all good", %{params: params} do
- assert %Changeset{valid?: true} = EconomicEvent.chgset(params)
+ assert %Changeset{valid?: true} = EconomicEvent.changeset(params)
end
test "fail when `:provider` and `:receiver` differ", %{params: params} do
@@ -308,7 +312,7 @@ describe "`chgset/1` with consume:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:provider_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -317,7 +321,7 @@ describe "`chgset/1` with consume:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:receiver_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -340,27 +344,27 @@ describe "`chgset/1` with use:" do
}}
end
- test "fail without `:resource_conforms_to` and `:resource_inventoried_as`", %{params: params} do
- assert %Changeset{valid?: false} = cset = EconomicEvent.chgset(params)
+ test "fail without `:resource_conforms_to` and `:resource_inventoried_as`",
+ %{params: params, errmsg_exist_xnor: errmsg} do
+ assert %Changeset{valid?: false} = cset = EconomicEvent.changeset(params)
err = Changeset.traverse_errors(cset, &elem(&1, 0))
- msg = "these are mutually exclusive and exactly one must be provided"
- assert {[^msg], err} = pop_in(err[:resource_conforms_to_id])
- assert {[^msg], err} = pop_in(err[:resource_inventoried_as_id])
+ assert {[^errmsg], err} = pop_in(err[:resource_conforms_to_id])
+ assert {[^errmsg], err} = pop_in(err[:resource_inventoried_as_id])
assert err == %{}
end
- test "fail with `:resource_conforms_to` and `:resource_inventoried_as`", %{params: params} do
+ test "fail with `:resource_conforms_to` and `:resource_inventoried_as`",
+ %{params: params, errmsg_exist_xnor: errmsg} do
res = Factory.insert!(:economic_resource)
spec = Factory.insert!(:resource_specification)
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:resource_inventoried_as_id, res.id)
|> Map.put(:resource_conforms_to_id, spec.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
- msg = "these are mutually exclusive and exactly one must be provided"
- assert {[^msg], err} = pop_in(err[:resource_conforms_to_id])
- assert {[^msg], err} = pop_in(err[:resource_inventoried_as_id])
+ assert {[^errmsg], err} = pop_in(err[:resource_conforms_to_id])
+ assert {[^errmsg], err} = pop_in(err[:resource_inventoried_as_id])
assert err == %{}
end
@@ -369,7 +373,7 @@ describe "`chgset/1` with use:" do
assert %Changeset{valid?: true} =
params
|> Map.put(:resource_conforms_to_id, spec.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
end
test "pass with `:resource_inventoried_as`", %{params: params} do
@@ -377,12 +381,12 @@ describe "`chgset/1` with use:" do
assert %Changeset{valid?: true} =
params
|> Map.put(:resource_inventoried_as_id, res.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
end
end
test "`chgset/1` with work: pass when all good" do
- assert %Changeset{valid?: true} = EconomicEvent.chgset(%{
+ assert %Changeset{valid?: true} = EconomicEvent.changeset(%{
action_id: "work",
input_of_id: Factory.insert!(:process).id,
provider_id: Factory.insert!(:agent).id,
@@ -416,7 +420,7 @@ describe "`chgset/1` with cite:" do
assert %Changeset{valid?: true} =
params
|> Map.put(:resource_conforms_to_id, spec.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
end
test "pass with `:resource_inventoried_as`", %{params: params} do
@@ -424,30 +428,30 @@ describe "`chgset/1` with cite:" do
assert %Changeset{valid?: true} =
params
|> Map.put(:resource_inventoried_as_id, res.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
end
- test "fail without `:resource_conforms_to` and `:resource_inventoried_as`", %{params: params} do
- assert %Changeset{valid?: false} = cset = EconomicEvent.chgset(params)
+ test "fail without `:resource_conforms_to` and `:resource_inventoried_as`",
+ %{params: params, errmsg_exist_xnor: errmsg} do
+ assert %Changeset{valid?: false} = cset = EconomicEvent.changeset(params)
err = Changeset.traverse_errors(cset, &elem(&1, 0))
- msg = "these are mutually exclusive and exactly one must be provided"
- assert {[^msg], err} = pop_in(err[:resource_conforms_to_id])
- assert {[^msg], err} = pop_in(err[:resource_inventoried_as_id])
+ assert {[^errmsg], err} = pop_in(err[:resource_conforms_to_id])
+ assert {[^errmsg], err} = pop_in(err[:resource_inventoried_as_id])
assert err == %{}
end
- test "fail with `:resource_conforms_to` and `:resource_inventoried_as`", %{params: params} do
+ test "fail with `:resource_conforms_to` and `:resource_inventoried_as`",
+ %{params: params, errmsg_exist_xnor: errmsg} do
res = Factory.insert!(:economic_resource)
spec = Factory.insert!(:resource_specification)
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:resource_inventoried_as_id, res.id)
|> Map.put(:resource_conforms_to_id, spec.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
- msg = "these are mutually exclusive and exactly one must be provided"
- assert {[^msg], err} = pop_in(err[:resource_conforms_to_id])
- assert {[^msg], err} = pop_in(err[:resource_inventoried_as_id])
+ assert {[^errmsg], err} = pop_in(err[:resource_conforms_to_id])
+ assert {[^errmsg], err} = pop_in(err[:resource_inventoried_as_id])
assert err == %{}
end
end
@@ -470,30 +474,30 @@ describe "`chgset/1` with deliverService:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:output_of_id, params.input_of_id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
- msg = "must have different processes"
+ msg = "all of them must be different"
assert {[^msg], err} = pop_in(err[:input_of_id])
assert {[^msg], err} = pop_in(err[:output_of_id])
assert err == %{}
end
test "pass with `:input_of` and `:output_of` differ", %{params: params} do
- assert %Changeset{valid?: true} = EconomicEvent.chgset(params)
+ assert %Changeset{valid?: true} = EconomicEvent.changeset(params)
end
test "pass without `:input_of`", %{params: params} do
assert %Changeset{valid?: true} =
params
|> Map.delete(:input_of)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
end
test "pass without `:output_of`", %{params: params} do
assert %Changeset{valid?: true} =
params
|> Map.delete(:output_of)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
end
end
@@ -516,7 +520,7 @@ describe "`chgset/1` with pickup:" do
end
test "pass when all good", %{params: params} do
- assert %Changeset{valid?: true} = EconomicEvent.chgset(params)
+ assert %Changeset{valid?: true} = EconomicEvent.changeset(params)
end
test "fail when `:provider` and `:receiver` differ", %{params: params} do
@@ -525,7 +529,7 @@ describe "`chgset/1` with pickup:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:provider_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -534,7 +538,7 @@ describe "`chgset/1` with pickup:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:receiver_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -562,7 +566,7 @@ describe "`chgset/1` with dropoff:" do
end
test "pass when all good", %{params: params} do
- assert %Changeset{valid?: true} = EconomicEvent.chgset(params)
+ assert %Changeset{valid?: true} = EconomicEvent.changeset(params)
end
test "fail when `:provider` and `:receiver` differ", %{params: params} do
@@ -571,7 +575,7 @@ describe "`chgset/1` with dropoff:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:provider_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -580,7 +584,7 @@ describe "`chgset/1` with dropoff:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:receiver_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -607,7 +611,7 @@ describe "`chgset/1` with accept:" do
end
test "pass when all good", %{params: params} do
- assert %Changeset{valid?: true} = EconomicEvent.chgset(params)
+ assert %Changeset{valid?: true} = EconomicEvent.changeset(params)
end
test "fail when `:provider` and `:receiver` differ", %{params: params} do
@@ -616,7 +620,7 @@ describe "`chgset/1` with accept:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:provider_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -625,7 +629,7 @@ describe "`chgset/1` with accept:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:receiver_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -653,7 +657,7 @@ describe "`chgset/1` with modify:" do
end
test "pass when all good", %{params: params} do
- assert %Changeset{valid?: true} = EconomicEvent.chgset(params)
+ assert %Changeset{valid?: true} = EconomicEvent.changeset(params)
end
test "fail when `:provider` and `:receiver` differ", %{params: params} do
@@ -662,7 +666,7 @@ describe "`chgset/1` with modify:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:provider_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -671,7 +675,7 @@ describe "`chgset/1` with modify:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:receiver_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -680,7 +684,7 @@ describe "`chgset/1` with modify:" do
end
test "`chgset/1` with transferCustody: pass when all good" do
- assert %Changeset{valid?: true} = EconomicEvent.chgset(%{
+ assert %Changeset{valid?: true} = EconomicEvent.changeset(%{
action_id: "transferCustody",
provider_id: Factory.insert!(:agent).id,
receiver_id: Factory.insert!(:agent).id,
@@ -694,7 +698,7 @@ test "`chgset/1` with transferCustody: pass when all good" do
end
test "`chgset/1` with transferAllRights: pass when all good" do
- assert %Changeset{valid?: true} = EconomicEvent.chgset(%{
+ assert %Changeset{valid?: true} = EconomicEvent.changeset(%{
action_id: "transferAllRights",
provider_id: Factory.insert!(:agent).id,
receiver_id: Factory.insert!(:agent).id,
@@ -709,7 +713,7 @@ test "`chgset/1` with transferAllRights: pass when all good" do
end
test "`chgset/1` with transfer: pass when all good" do
- assert %Changeset{valid?: true} = EconomicEvent.chgset(%{
+ assert %Changeset{valid?: true} = EconomicEvent.changeset(%{
action_id: "transfer",
provider_id: Factory.insert!(:agent).id,
receiver_id: Factory.insert!(:agent).id,
@@ -741,7 +745,7 @@ describe "`chgset/1` with move:" do
end
test "pass when all good", %{params: params} do
- assert %Changeset{valid?: true} = EconomicEvent.chgset(params)
+ assert %Changeset{valid?: true} = EconomicEvent.changeset(params)
end
test "fail when `:provider` and `:receiver` differ", %{params: params} do
@@ -750,7 +754,7 @@ describe "`chgset/1` with move:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:provider_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -759,7 +763,7 @@ describe "`chgset/1` with move:" do
assert %Changeset{valid?: false} = cset =
params
|> Map.put(:receiver_id, agent.id)
- |> EconomicEvent.chgset()
+ |> EconomicEvent.changeset()
err = Changeset.traverse_errors(cset, &elem(&1, 0))
assert {[_], err} = pop_in(err[:provider_id])
assert {[_], err} = pop_in(err[:receiver_id])
@@ -777,7 +781,7 @@ describe "" do
# assert {:error, %Changeset{errors: errs}} =
# params
- # |> EconomicEvent.chgset()
+ # |> EconomicEvent.changeset()
# |> Repo.insert()
# assert {:ok, _} = Keyword.fetch(errs, :resource_conforms_to_id)
@@ -793,7 +797,7 @@ describe "" do
# assert {:error, %Changeset{errors: errs}} =
# params
- # |> EconomicEvent.chgset()
+ # |> EconomicEvent.changeset()
# |> Repo.insert()
# assert {:ok, _} = Keyword.fetch(errs, :resource_conforms_to_id)
@@ -808,7 +812,7 @@ describe "" do
# assert {:ok, %EconomicEvent{} = eco_evt} =
# params
- # |> EconomicEvent.chgset()
+ # |> EconomicEvent.changeset()
# |> Repo.insert()
# assert eco_evt.action_id == params.action_id
@@ -843,7 +847,7 @@ describe "" do
# assert {:ok, %EconomicEvent{} = eco_evt} =
# params
- # |> EconomicEvent.chgset()
+ # |> EconomicEvent.changeset()
# |> Repo.insert()
# assert eco_evt.action_id == params.action_id
@@ -879,7 +883,7 @@ describe "" do
# assert {:ok, %EconomicEvent{} = eco_evt} =
# params
- # |> EconomicEvent.chgset()
+ # |> EconomicEvent.changeset()
# |> Repo.insert()
# assert eco_evt.action_id == params.action_id
@@ -914,7 +918,7 @@ describe "" do
# assert {:ok, %EconomicEvent{} = eco_evt} =
# params
- # |> EconomicEvent.chgset()
+ # |> EconomicEvent.changeset()
# |> Repo.insert()
# assert eco_evt.action_id == params.action_id
@@ -948,7 +952,7 @@ test "update EconomicEvent", %{params: _params} do
# assert {:ok, %EconomicEvent{} = new} =
# old
- # |> EconomicEvent.chgset(params)
+ # |> EconomicEvent.changeset(params)
# |> Repo.update()
# assert new.action_id == old.action_id
diff --git a/test/vf/economic_event/domain.test.exs b/test/vf/economic_event/domain.test.exs
@@ -43,9 +43,11 @@ setup ctx do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- has_end: DateTime.utc_now(),
+ has_end: Factory.now(),
}
- assert {:ok, _, res, _} = Domain.create(params, %{name: Factory.str("name")})
+ assert %EconomicEvent{resource_inventoried_as: res} =
+ Domain.create!(params, %{name: Factory.str("name")})
+ |> Domain.preload(:resource_inventoried_as)
if ctx[:want_contained] || ctx[:want_container] do
agent = Factory.insert!(:agent)
@@ -58,17 +60,19 @@ setup ctx do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- has_end: DateTime.utc_now(),
+ has_end: Factory.now(),
}
- assert {:ok, _, tmp_res, _} = Domain.create(params, %{name: Factory.str("name")})
+ assert %EconomicEvent{resource_inventoried_as: tmp_res} =
+ Domain.create!(params, %{name: Factory.str("name")})
+ |> Domain.preload(:resource_inventoried_as)
# TODO: use combine-separate when implemented instead
if ctx[:want_contained] do
- assert {:ok, _} = Changeset.change(res, contained_in_id: tmp_res.id) |> Repo.update()
+ Changeset.change(res, contained_in_id: tmp_res.id) |> Repo.update!()
end
if ctx[:want_container] do
- assert {:ok, _} = Changeset.change(tmp_res, contained_in_id: res.id) |> Repo.update()
+ Changeset.change(tmp_res, contained_in_id: res.id) |> Repo.update!()
end
end
@@ -96,7 +100,7 @@ describe "`create/2` with raise:" do
has_unit_id: res.accounting_quantity_has_unit_id,
has_numerical_value: Factory.float(),
},
- has_point_in_time: DateTime.utc_now(),
+ has_point_in_time: Factory.now(),
}}
end
end
@@ -113,7 +117,7 @@ describe "`create/2` with raise:" do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- has_end: DateTime.utc_now(),
+ has_end: Factory.now(),
to_location_id: Factory.insert!(:spatial_thing).id,
}
res_params = %{
@@ -129,8 +133,10 @@ describe "`create/2` with raise:" do
license: Factory.str("license"),
metadata: %{Factory.str("key") => Factory.str("val")},
}
- assert {:ok, %EconomicEvent{}, %EconomicResource{} = res, _} =
+ assert {:ok, %EconomicEvent{} = evt} =
Domain.create(evt_params, res_params)
+ evt = Domain.preload(evt, :resource_inventoried_as)
+ res = evt.resource_inventoried_as
assert res.name == res_params.name
assert res.note == res_params.note
@@ -154,9 +160,9 @@ describe "`create/2` with raise:" do
end
test "pass with `:resource_inventoried_as`", %{params: params} do
- {:ok, res_before} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
- assert {:ok, %EconomicEvent{}} = Domain.create(params, nil)
- {:ok, res_after} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ res_before = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{}} = Domain.create(params)
+ res_after = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
assert res_after.accounting_quantity_has_numerical_value ==
res_before.accounting_quantity_has_numerical_value + params.resource_quantity.has_numerical_value
@@ -171,23 +177,23 @@ describe "`create/2` with raise:" do
|> Map.put(:provider_id, agent.id)
|> Map.put(:receiver_id, agent.id)
assert {:error, "you don't have ownership over this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when event's unit and resource's unit differ", %{params: params} do
params = update_in(params.resource_quantity.has_unit_id, fn _ -> Factory.insert!(:unit).id end)
assert {:error, "the unit of resource quantity must match with the unit of this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_contained
test "fail when the resource is a contained resource", %{params: params} do
- assert {:error, "you can't raise into a contained resource"} = Domain.create(params, nil)
+ assert {:error, "you can't raise into a contained resource"} = Domain.create(params)
end
@tag :want_container
test "fail when the resource is a container resource", %{params: params} do
- assert {:error, "you can't raise into a container resource"} = Domain.create(params, nil)
+ assert {:error, "you can't raise into a container resource"} = Domain.create(params)
end
end
@@ -207,7 +213,7 @@ describe "`create/2` with produce:" do
has_unit_id: res.accounting_quantity_has_unit_id,
has_numerical_value: Factory.float(),
},
- has_beginning: DateTime.utc_now(),
+ has_beginning: Factory.now(),
}}
end
end
@@ -225,7 +231,7 @@ describe "`create/2` with produce:" do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- has_end: DateTime.utc_now(),
+ has_end: Factory.now(),
to_location_id: Factory.insert!(:spatial_thing).id,
}
res_params = %{
@@ -241,8 +247,10 @@ describe "`create/2` with produce:" do
license: Factory.str("license"),
metadata: %{Factory.str("key") => Factory.str("val")},
}
- assert {:ok, %EconomicEvent{}, %EconomicResource{} = res, _} =
+ assert {:ok, %EconomicEvent{} = evt} =
Domain.create(evt_params, res_params)
+ evt = Domain.preload(evt, :resource_inventoried_as)
+ res = evt.resource_inventoried_as
assert res.name == res_params.name
assert res.note == res_params.note
@@ -266,9 +274,9 @@ describe "`create/2` with produce:" do
end
test "pass with `:resource_inventoried_as`", %{params: params} do
- {:ok, res_before} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
- assert {:ok, %EconomicEvent{}} = Domain.create(params, nil)
- {:ok, res_after} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ res_before = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{}} = Domain.create(params)
+ res_after = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
assert res_after.accounting_quantity_has_numerical_value ==
res_before.accounting_quantity_has_numerical_value + params.resource_quantity.has_numerical_value
@@ -283,23 +291,23 @@ describe "`create/2` with produce:" do
|> Map.put(:provider_id, agent.id)
|> Map.put(:receiver_id, agent.id)
assert {:error, "you don't have ownership over this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when event's unit and resource's unit differ", %{params: params} do
params = update_in(params.resource_quantity.has_unit_id, fn _ -> Factory.insert!(:unit).id end)
assert {:error, "the unit of resource quantity must match with the unit of this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_contained
test "fail when the resource is a contained resource", %{params: params} do
- assert {:error, "you can't produce into a contained resource"} = Domain.create(params, nil)
+ assert {:error, "you can't produce into a contained resource"} = Domain.create(params)
end
@tag :want_container
test "fail when the resource is a container resource", %{params: params} do
- assert {:error, "you can't produce into a container resource"} = Domain.create(params, nil)
+ assert {:error, "you can't produce into a container resource"} = Domain.create(params)
end
end
@@ -314,14 +322,14 @@ describe "`create/2` with lower:" do
has_unit_id: res.accounting_quantity_has_unit_id,
has_numerical_value: Factory.float(),
},
- has_end: DateTime.utc_now(),
+ has_end: Factory.now(),
}}
end
test "pass when all good", %{params: params} do
- {:ok, res_before} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
- assert {:ok, %EconomicEvent{}} = Domain.create(params, nil)
- {:ok, res_after} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ res_before = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{}} = Domain.create(params)
+ res_after = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
assert res_after.accounting_quantity_has_numerical_value ==
res_before.accounting_quantity_has_numerical_value - params.resource_quantity.has_numerical_value
assert res_after.onhand_quantity_has_numerical_value ==
@@ -335,23 +343,23 @@ describe "`create/2` with lower:" do
|> Map.put(:provider_id, agent.id)
|> Map.put(:receiver_id, agent.id)
assert {:error, "you don't have ownership over this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when event's unit and resource's unit differ", %{params: params} do
params = update_in(params.resource_quantity.has_unit_id, fn _ -> Factory.insert!(:unit).id end)
assert {:error, "the unit of resource quantity must match with the unit of this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_contained
test "fail when the resource is a contained resource", %{params: params} do
- assert {:error, "you can't lower a contained resource"} = Domain.create(params, nil)
+ assert {:error, "you can't lower a contained resource"} = Domain.create(params)
end
@tag :want_container
test "fail when the resource is a container resource", %{params: params} do
- assert {:error, "you can't lower a container resource"} = Domain.create(params, nil)
+ assert {:error, "you can't lower a container resource"} = Domain.create(params)
end
end
@@ -367,15 +375,15 @@ describe "`create/2` with consume:" do
has_unit_id: res.accounting_quantity_has_unit_id,
has_numerical_value: Factory.float(),
},
- has_beginning: DateTime.utc_now(),
- has_end: DateTime.utc_now(),
+ has_beginning: Factory.now(),
+ has_end: Factory.now(),
}}
end
test "pass when all good", %{params: params} do
- {:ok, res_before} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
- assert {:ok, %EconomicEvent{}} = Domain.create(params, nil)
- {:ok, res_after} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ res_before = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{}} = Domain.create(params)
+ res_after = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
assert res_after.accounting_quantity_has_numerical_value ==
res_before.accounting_quantity_has_numerical_value - params.resource_quantity.has_numerical_value
@@ -390,23 +398,23 @@ describe "`create/2` with consume:" do
|> Map.put(:provider_id, agent.id)
|> Map.put(:receiver_id, agent.id)
assert {:error, "you don't have ownership over this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when event's unit and resource's unit differ", %{params: params} do
params = update_in(params.resource_quantity.has_unit_id, fn _ -> Factory.insert!(:unit).id end)
assert {:error, "the unit of resource quantity must match with the unit of this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_contained
test "fail when the resource is a contained resource", %{params: params} do
- assert {:error, "you can't consume a contained resource"} = Domain.create(params, nil)
+ assert {:error, "you can't consume a contained resource"} = Domain.create(params)
end
@tag :want_container
test "fail when the resource is a container resource", %{params: params} do
- assert {:error, "you can't consume a container resource"} = Domain.create(params, nil)
+ assert {:error, "you can't consume a container resource"} = Domain.create(params)
end
end
@@ -426,28 +434,28 @@ describe "`create/2` with use:" do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- has_point_in_time: DateTime.utc_now(),
+ has_point_in_time: Factory.now(),
}}
end
test "pass when all good", %{params: params} do
- assert {:ok, %EconomicEvent{}} = Domain.create(params, nil)
+ assert {:ok, %EconomicEvent{}} = Domain.create(params)
end
test "fail when the event's resource quantity unit doesn't match with the resource's", %{params: params} do
assert {:error, "the unit of resource quantity must match with the unit of this resource"} =
update_in(params.resource_quantity.has_unit_id, fn _ -> Factory.insert!(:unit).id end)
- |> Domain.create(nil)
+ |> Domain.create()
end
@tag :want_contained
test "fail when the resource is a contained resource", %{params: params} do
- assert {:error, "you can't use a contained resource"} = Domain.create(params, nil)
+ assert {:error, "you can't use a contained resource"} = Domain.create(params)
end
@tag :want_container
test "fail when the resource is a container resource", %{params: params} do
- assert {:error, "you can't use a container resource"} = Domain.create(params, nil)
+ assert {:error, "you can't use a container resource"} = Domain.create(params)
end
end
@@ -463,12 +471,12 @@ describe "`create/2` with pickup:" do
has_unit_id: res.onhand_quantity_has_unit_id,
has_numerical_value: res.onhand_quantity_has_numerical_value,
},
- has_beginning: DateTime.utc_now(),
+ has_beginning: Factory.now(),
}}
end
test "pass when all good", %{params: params} do
- assert {:ok, %EconomicEvent{}} = Domain.create(params, nil)
+ assert {:ok, %EconomicEvent{}} = Domain.create(params)
end
test "fail when provider doesn't have custody over the resource", %{params: params} do
@@ -478,48 +486,49 @@ describe "`create/2` with pickup:" do
|> Map.put(:provider_id, agent.id)
|> Map.put(:receiver_id, agent.id)
assert {:error, "you don't have custody over this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_contained
test "fail when the resource is a contained resource", %{params: params} do
assert {:error, "you can't pickup a contained resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when event's unit and resource's unit differ", %{params: params} do
params = update_in(params.resource_quantity.has_unit_id, fn _ -> Factory.insert!(:unit).id end)
assert {:error, "the unit of resource quantity must match with the unit of this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when event's quantity value and resource's onhand-quantity value differ", %{params: params} do
params = update_in(params.resource_quantity.has_numerical_value, &(&1 + 1))
assert {:error, "the pickup events need to fully pickup the resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when more than one pickup event references the same resource in the same process", %{params: params} do
- assert {:ok, _} = Domain.create(params, nil)
+ assert {:ok, _} = Domain.create(params)
assert {:error, "no more than one pickup event in the same process, referring to the same resource is allowed"}
- = Domain.create(params, nil)
+ = Domain.create(params)
end
end
describe "`create/2` with dropoff:" do
setup %{res: res} do
- assert {:ok, pair_evt} = Domain.create(%{
- action_id: "pickup",
- input_of_id: Factory.insert!(:process).id,
- provider_id: res.custodian_id,
- receiver_id: res.custodian_id,
- resource_inventoried_as_id: res.id,
- resource_quantity: %{
- has_unit_id: res.onhand_quantity_has_unit_id,
- has_numerical_value: res.onhand_quantity_has_numerical_value,
- },
- has_end: DateTime.utc_now(),
- }, nil)
+ assert %EconomicEvent{} = pair_evt =
+ Domain.create!(%{
+ action_id: "pickup",
+ input_of_id: Factory.insert!(:process).id,
+ provider_id: res.custodian_id,
+ receiver_id: res.custodian_id,
+ resource_inventoried_as_id: res.id,
+ resource_quantity: %{
+ has_unit_id: res.onhand_quantity_has_unit_id,
+ has_numerical_value: res.onhand_quantity_has_numerical_value,
+ },
+ has_end: Factory.now(),
+ })
%{params: %{
action_id: "dropoff",
@@ -531,15 +540,16 @@ describe "`create/2` with dropoff:" do
has_unit_id: pair_evt.resource_quantity_has_unit_id,
has_numerical_value: pair_evt.resource_quantity_has_numerical_value,
},
- has_beginning: DateTime.utc_now(),
- has_end: DateTime.utc_now(),
+ has_beginning: Factory.now(),
+ has_end: Factory.now(),
to_location_id: Factory.insert!(:spatial_thing).id,
}}
end
test "pass when all good", %{params: params} do
- assert {:ok, %EconomicEvent{} = evt} = Domain.create(params, nil)
- {:ok, res} = EconomicResource.Domain.one(evt.resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{} = evt} = Domain.create(params)
+ evt = Domain.preload(evt, :resource_inventoried_as)
+ res = evt.resource_inventoried_as
assert res.current_location_id == params.to_location_id
end
@@ -550,26 +560,26 @@ describe "`create/2` with dropoff:" do
|> Map.put(:provider_id, agent.id)
|> Map.put(:receiver_id, agent.id)
assert {:error, "you don't have custody over this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when event's unit and paired event's unit differ", %{params: params} do
params = update_in(params.resource_quantity.has_unit_id, fn _ -> Factory.insert!(:unit).id end)
assert {:error, "the unit of resource quantity must match with the unit of the paired event"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_container
test "fail when the resource is a container and event's quantity value and resource's onhand-quantity value differ", %{params: params} do
params = update_in(params.resource_quantity.has_numerical_value, &(&1 + 1))
assert {:error, "the dropoff events need to fully dropoff the resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when more than one dropoff event references the same resource in the same process", %{params: params} do
- assert {:ok, _} = Domain.create(params, nil)
+ assert {:ok, _} = Domain.create(params)
assert {:error, "no more than one dropoff event in the same process, referring to the same resource is allowed"}
- = Domain.create(params, nil)
+ = Domain.create(params)
end
end
@@ -585,14 +595,14 @@ describe "`create/2` with accept:" do
has_unit_id: res.onhand_quantity_has_unit_id,
has_numerical_value: res.onhand_quantity_has_numerical_value,
},
- has_point_in_time: DateTime.utc_now(),
+ has_point_in_time: Factory.now(),
}}
end
test "pass when all good", %{params: params} do
- {:ok, res_before} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
- assert {:ok, %EconomicEvent{}} = Domain.create(params, nil)
- {:ok, res_after} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ res_before = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{}} = Domain.create(params)
+ res_after = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
assert res_after.accounting_quantity_has_numerical_value ==
res_before.accounting_quantity_has_numerical_value
@@ -607,68 +617,67 @@ describe "`create/2` with accept:" do
|> Map.put(:provider_id, agent.id)
|> Map.put(:receiver_id, agent.id)
assert {:error, "you don't have custody over this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_contained
test "fail when the resource is a contained resource", %{params: params} do
assert {:error, "you can't accept a contained resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when event's unit and resource's unit differ", %{params: params} do
params = update_in(params.resource_quantity.has_unit_id, fn _ -> Factory.insert!(:unit).id end)
assert {:error, "the unit of resource quantity must match with the unit of this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when event's quantity value and resource's onhand-quantity value differ", %{params: params} do
params = update_in(params.resource_quantity.has_numerical_value, &(&1 + 1))
assert {:error, "the accept events need to fully accept the resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag skip: "TODO: use combine-separate when implemented"
@tag :want_combine
test "fail when the there are any combine events", %{params: params} do
assert {:error, "you can't add another accept event to the same process where there are at least one combine or separate events"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag skip: "TODO: use combine-separate when implemented"
@tag :want_combine
test "fail when the there are any separate events", %{params: params} do
assert {:error, "you can't add another accept event to the same process where there are at least one combine or separate events"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when more than one accept event references the same resource in the same process", %{params: params} do
- assert {:ok, %EconomicEvent{}} = Domain.create(params, nil)
+ assert {:ok, %EconomicEvent{}} = Domain.create(params)
# in order to satisfy the fact that they it should fully
# accept the resource
- params
- |> Map.put(:action_id, "raise")
- |> Domain.create(nil)
+ params |> Map.put(:action_id, "raise") |> Domain.create()
assert {:error, "no more than one accept event in the same process, referring to the same resource is allowed"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
end
describe "`create/2` with modify:" do
setup %{res: res} do
- assert {:ok, pair_evt} = Domain.create(%{
- action_id: "accept",
- input_of_id: Factory.insert!(:process).id,
- provider_id: res.custodian_id,
- receiver_id: res.custodian_id,
- resource_inventoried_as_id: res.id,
- resource_quantity: %{
- has_unit_id: res.onhand_quantity_has_unit_id,
- has_numerical_value: res.onhand_quantity_has_numerical_value,
- },
- has_end: DateTime.utc_now(),
- }, nil)
+ assert %EconomicEvent{} = pair_evt =
+ Domain.create!(%{
+ action_id: "accept",
+ input_of_id: Factory.insert!(:process).id,
+ provider_id: res.custodian_id,
+ receiver_id: res.custodian_id,
+ resource_inventoried_as_id: res.id,
+ resource_quantity: %{
+ has_unit_id: res.onhand_quantity_has_unit_id,
+ has_numerical_value: res.onhand_quantity_has_numerical_value,
+ },
+ has_end: Factory.now(),
+ })
%{params: %{
action_id: "modify",
@@ -680,18 +689,18 @@ describe "`create/2` with modify:" do
has_unit_id: pair_evt.resource_quantity_has_unit_id,
has_numerical_value: pair_evt.resource_quantity_has_numerical_value,
},
- has_beginning: DateTime.utc_now(),
- has_end: DateTime.utc_now(),
+ has_beginning: Factory.now(),
+ has_end: Factory.now(),
}}
end
test "pass when all good", %{params: params} do
- {:ok, res_before} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ res_before = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
assert res_before.stage_id == nil
- assert {:ok, %EconomicEvent{}} = Domain.create(params, nil)
- {:ok, res_after} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
- {:ok, proc} = Process.Domain.one(params.output_of_id)
+ assert {:ok, %EconomicEvent{}} = Domain.create(params)
+ res_after = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
+ proc = Process.Domain.one!(params.output_of_id)
assert res_after.stage_id == proc.based_on_id
assert res_after.accounting_quantity_has_numerical_value ==
res_before.accounting_quantity_has_numerical_value
@@ -706,7 +715,7 @@ describe "`create/2` with modify:" do
|> Map.put(:provider_id, agent.id)
|> Map.put(:receiver_id, agent.id)
assert {:error, "you don't have custody over this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when event's unit and paired event's unit differ", %{params: params} do
@@ -714,25 +723,23 @@ describe "`create/2` with modify:" do
Factory.insert!(:unit).id
end)
assert {:error, "the unit of resource quantity must match with the unit of the paired event"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when event's quantity value and resource's onhand-quantity value differ", %{params: params} do
params = update_in(params.resource_quantity.has_numerical_value, &(&1 + 1))
assert {:error, "the modify events need to fully modify the resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when more than one modify event references the same resource in the same process", %{params: params} do
- assert {:ok, %EconomicEvent{}} = Domain.create(params, nil)
+ assert {:ok, %EconomicEvent{}} = Domain.create(params)
# in order to satisfy the fact that they it should fully
# modify the resource
- params
- |> Map.put(:action_id, "raise")
- |> Domain.create(nil)
+ params |> Map.put(:action_id, "raise") |> Domain.create!()
assert {:error, "no more than one modify event in the same process, referring to the same resource is allowed"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
end
@@ -749,21 +756,22 @@ describe "`create/2` with transferCustody:" do
has_unit_id: res.accounting_quantity_has_unit_id,
has_numerical_value: Factory.float(),
},
- has_point_in_time: DateTime.utc_now(),
+ has_point_in_time: Factory.now(),
}
- assert {:ok, _, to_res, _} = Domain.create(params, %{name: Factory.str("name")})
+ assert %EconomicEvent{resource_inventoried_as_id: to_res_id} =
+ Domain.create!(params, %{name: Factory.str("name")})
%{params: %{
action_id: "transferCustody",
provider_id: res.custodian_id,
receiver_id: Factory.insert!(:agent).id,
resource_inventoried_as_id: res.id,
- to_resource_inventoried_as_id: to_res.id,
+ to_resource_inventoried_as_id: to_res_id,
resource_quantity: %{
has_unit_id: res.onhand_quantity_has_unit_id,
has_numerical_value: res.onhand_quantity_has_numerical_value,
},
- has_beginning: DateTime.utc_now(),
+ has_beginning: Factory.now(),
}}
else
%{params: %{
@@ -776,13 +784,13 @@ describe "`create/2` with transferCustody:" do
has_numerical_value: res.onhand_quantity_has_numerical_value,
},
to_location_id: Factory.insert!(:spatial_thing).id,
- has_beginning: DateTime.utc_now(),
+ has_beginning: Factory.now(),
}}
end
end
test "pass without `:to_resource_inventoried_as`", %{params: params} do
- {:ok, res_before} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ res_before = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
contained_ids = Enum.map(0..9, fn _ ->
agent = Factory.insert!(:agent)
@@ -795,11 +803,15 @@ describe "`create/2` with transferCustody:" do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- has_end: DateTime.utc_now(),
+ has_end: Factory.now(),
}
- assert {:ok, _, tmp_res, _} = Domain.create(raise_params, %{name: Factory.str("name")})
- assert {:ok, _} =
- Changeset.change(tmp_res, contained_in_id: params.resource_inventoried_as_id) |> Repo.update()
+ assert %EconomicEvent{} = evt =
+ Domain.create!(raise_params, %{name: Factory.str("name")})
+ evt = Domain.preload(evt, :resource_inventoried_as)
+ tmp_res = evt.resource_inventoried_as
+
+ Changeset.change(tmp_res, contained_in_id: params.resource_inventoried_as_id)
+ |> Repo.update!()
tmp_res.id
end)
@@ -817,8 +829,10 @@ describe "`create/2` with transferCustody:" do
license: Factory.str("license"),
metadata: %{Factory.str("key") => Factory.str("val")},
}
- assert {:ok, %EconomicEvent{} = evt, _, %EconomicResource{} = to_res} = Domain.create(params, res_params)
- {:ok, res_after} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{} = evt} = Domain.create(params, res_params)
+ evt = Domain.preload(evt, :to_resource_inventoried_as)
+ to_res = evt.to_resource_inventoried_as
+ res_after = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
assert res_after.accounting_quantity_has_numerical_value ==
res_before.accounting_quantity_has_numerical_value
@@ -857,12 +871,12 @@ describe "`create/2` with transferCustody:" do
@tag :want_to_resource
test "pass with `:to_resource_inventoried_as`", %{params: params} do
- {:ok, res_before} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
- {:ok, to_res_before} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
+ res_before = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
+ to_res_before = EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
- assert {:ok, %EconomicEvent{}} = Domain.create(params, nil)
- {:ok, res_after} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
- {:ok, to_res_after} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{}} = Domain.create(params)
+ res_after = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
+ to_res_after = EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
assert res_after.accounting_quantity_has_numerical_value ==
res_before.accounting_quantity_has_numerical_value
@@ -882,45 +896,45 @@ describe "`create/2` with transferCustody:" do
|> Map.put(:provider_id, agent.id)
|> Map.put(:receiver_id, agent.id)
assert {:error, "you don't have custody over this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_contained
test "fail when the resource is a contained resource", %{params: params} do
assert {:error, "you can't transfer-custody a contained resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when event's unit and resource's unit differ", %{params: params} do
params = update_in(params.resource_quantity.has_unit_id, fn _ -> Factory.insert!(:unit).id end)
assert {:error, "the unit of resource-quantity must match with the unit of resource-inventoried-as"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_container
test "fail when the resource is a container and onhand-quantity is non-positive", %{params: params} do
err = "the transfer-custody events need container resources to have positive onhand-quantity"
- {:ok, res} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ res = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
Changeset.change(res, onhand_quantity_has_numerical_value: 0.0) |> Repo.update!()
- assert {:error, ^err} = Domain.create(params, nil)
+ assert {:error, ^err} = Domain.create(params)
Changeset.change(res, onhand_quantity_has_numerical_value: -1.0) |> Repo.update!()
- assert {:error, ^err} = Domain.create(params, nil)
+ assert {:error, ^err} = Domain.create(params)
end
@tag :want_container
test "fail when event's quantity value and resource's onhand-quantity value differ", %{params: params} do
params = update_in(params.resource_quantity.has_numerical_value, &(&1 + 1))
assert {:error, "the transfer-custody events need to fully transfer the resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_container
@tag :want_to_resource
test "fail when transferring a container resource into another resource", %{params: params} do
assert {:error, "you can't transfer-custody a container resource into another resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
@@ -935,18 +949,18 @@ describe "`create/2` with transferCustody:" do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- has_end: DateTime.utc_now(),
+ has_end: Factory.now(),
}
- assert {:ok, _, tmp_res, _} = Domain.create(raise_params, %{name: Factory.str("name")})
- # TODO: use combine-separate when implemented instead
- {:ok, res} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{resource_inventoried_as_id: tmp_res_id}} =
+ Domain.create(raise_params, %{name: Factory.str("name")})
- res
- |> Changeset.change(contained_in_id: tmp_res.id)
+ # TODO: use combine-separate when implemented instead
+ EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
+ |> Changeset.change(contained_in_id: tmp_res_id)
|> Repo.update!()
assert {:error, "you can't transfer-custody into a contained resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
@@ -961,39 +975,38 @@ describe "`create/2` with transferCustody:" do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- has_end: DateTime.utc_now(),
+ has_end: Factory.now(),
}
- assert {:ok, _, tmp_res, _} = Domain.create(raise_params, %{name: Factory.str("name")})
+ assert {:ok, %EconomicEvent{} = evt} =
+ Domain.create(raise_params, %{name: Factory.str("name")})
+ evt = Domain.preload(evt, :resource_inventoried_as)
+ tmp_res = evt.resource_inventoried_as
# TODO: use combine-separate when implemented instead
Changeset.change(tmp_res, contained_in_id: params.to_resource_inventoried_as_id)
- |> Repo.update()
+ |> Repo.update!()
assert {:error, "you can't transfer-custody into a container resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
test "fail when event's unit and to-resource's unit differ", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
|> Changeset.change(onhand_quantity_has_unit_id: Factory.insert!(:unit).id)
|> Repo.update!()
assert {:error, "the unit of resource-quantity must match with the unit of to-resource-inventoried-as"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
test "fail when resoure and to-resource don't conform to the same spec", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
|> Changeset.change(conforms_to_id: Factory.insert!(:resource_specification).id)
|> Repo.update!()
assert {:error, "the resources must conform to the same specification"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
end
@@ -1010,21 +1023,22 @@ describe "`create/2` with transferAllRights:" do
has_unit_id: res.accounting_quantity_has_unit_id,
has_numerical_value: Factory.float(),
},
- has_point_in_time: DateTime.utc_now(),
+ has_point_in_time: Factory.now(),
}
- assert {:ok, _, to_res, _} = Domain.create(params, %{name: Factory.str("name")})
+ assert %EconomicEvent{resource_inventoried_as_id: to_res_id} =
+ Domain.create!(params, %{name: Factory.str("name")})
%{params: %{
action_id: "transferAllRights",
provider_id: res.primary_accountable_id,
receiver_id: Factory.insert!(:agent).id,
resource_inventoried_as_id: res.id,
- to_resource_inventoried_as_id: to_res.id,
+ to_resource_inventoried_as_id: to_res_id,
resource_quantity: %{
has_unit_id: res.accounting_quantity_has_unit_id,
has_numerical_value: res.accounting_quantity_has_numerical_value,
},
- has_beginning: DateTime.utc_now(),
+ has_beginning: Factory.now(),
}}
else
%{params: %{
@@ -1036,13 +1050,13 @@ describe "`create/2` with transferAllRights:" do
has_unit_id: res.accounting_quantity_has_unit_id,
has_numerical_value: res.accounting_quantity_has_numerical_value,
},
- has_beginning: DateTime.utc_now(),
+ has_beginning: Factory.now(),
}}
end
end
test "pass without `:to_resource_inventoried_as`", %{params: params} do
- {:ok, res_before} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ res_before = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
contained_ids = Enum.map(0..9, fn _ ->
agent = Factory.insert!(:agent)
@@ -1055,11 +1069,14 @@ describe "`create/2` with transferAllRights:" do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- has_end: DateTime.utc_now(),
+ has_end: Factory.now(),
}
- assert {:ok, _, tmp_res, _} = Domain.create(raise_params, %{name: Factory.str("name")})
- assert {:ok, _} =
- Changeset.change(tmp_res, contained_in_id: params.resource_inventoried_as_id) |> Repo.update()
+ assert {:ok, %EconomicEvent{} = evt} =
+ Domain.create(raise_params, %{name: Factory.str("name")})
+ evt = Domain.preload(evt, :resource_inventoried_as)
+ tmp_res = evt.resource_inventoried_as
+ Changeset.change(tmp_res, contained_in_id: params.resource_inventoried_as_id)
+ |> Repo.update!()
tmp_res.id
end)
@@ -1077,8 +1094,11 @@ describe "`create/2` with transferAllRights:" do
license: Factory.str("license"),
metadata: %{Factory.str("key") => Factory.str("val")},
}
- assert {:ok, %EconomicEvent{} = evt, _, %EconomicResource{} = to_res} = Domain.create(params, res_params)
- {:ok, res_after} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{} = evt} =
+ Domain.create(params, res_params)
+ evt = Domain.preload(evt, :to_resource_inventoried_as)
+ to_res = evt.to_resource_inventoried_as
+ res_after = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
assert res_after.accounting_quantity_has_numerical_value ==
res_before.accounting_quantity_has_numerical_value - params.resource_quantity.has_numerical_value
@@ -1116,12 +1136,12 @@ describe "`create/2` with transferAllRights:" do
@tag :want_to_resource
test "pass with `:to_resource_inventoried_as`", %{params: params} do
- {:ok, res_before} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
- {:ok, to_res_before} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
+ res_before = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
+ to_res_before = EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
- assert {:ok, %EconomicEvent{}} = Domain.create(params, nil)
- {:ok, res_after} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
- {:ok, to_res_after} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{}} = Domain.create(params)
+ res_after = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
+ to_res_after = EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
assert res_after.accounting_quantity_has_numerical_value ==
res_before.accounting_quantity_has_numerical_value - params.resource_quantity.has_numerical_value
@@ -1141,45 +1161,45 @@ describe "`create/2` with transferAllRights:" do
|> Map.put(:provider_id, agent.id)
|> Map.put(:receiver_id, agent.id)
assert {:error, "you don't have accountability over this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_contained
test "fail when the resource is a contained resource", %{params: params} do
assert {:error, "you can't transfer-all-rights a contained resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when event's unit and resource's unit differ", %{params: params} do
params = update_in(params.resource_quantity.has_unit_id, fn _ -> Factory.insert!(:unit).id end)
assert {:error, "the unit of resource-quantity must match with the unit of resource-inventoried-as"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_container
test "fail when the resource is a container and accounting-quantity is non-positive", %{params: params} do
err = "the transfer-all-rights events need container resources to have positive accounting-quantity"
- {:ok, res} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ res = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
Changeset.change(res, accounting_quantity_has_numerical_value: 0.0) |> Repo.update!()
- assert {:error, ^err} = Domain.create(params, nil)
+ assert {:error, ^err} = Domain.create(params)
Changeset.change(res, accounting_quantity_has_numerical_value: -1.0) |> Repo.update!()
- assert {:error, ^err} = Domain.create(params, nil)
+ assert {:error, ^err} = Domain.create(params)
end
@tag :want_container
test "fail when event's quantity value and resource's accounting-quantity value differ", %{params: params} do
params = update_in(params.resource_quantity.has_numerical_value, &(&1 + 1))
assert {:error, "the transfer-all-rights events need to fully transfer the resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_container
@tag :want_to_resource
test "fail when transferring a container resource into another resource", %{params: params} do
assert {:error, "you can't transfer-all-rights a container resource into another resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
@@ -1194,18 +1214,18 @@ describe "`create/2` with transferAllRights:" do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- has_end: DateTime.utc_now(),
+ has_end: Factory.now(),
}
- assert {:ok, _, tmp_res, _} = Domain.create(raise_params, %{name: Factory.str("name")})
- # TODO: use combine-separate when implemented instead
- {:ok, res} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{resource_inventoried_as_id: tmp_res_id}} =
+ Domain.create(raise_params, %{name: Factory.str("name")})
- res
- |> Changeset.change(contained_in_id: tmp_res.id)
+ # TODO: use combine-separate when implemented instead
+ EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
+ |> Changeset.change(contained_in_id: tmp_res_id)
|> Repo.update!()
assert {:error, "you can't transfer-all-rights into a contained resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
@@ -1220,39 +1240,38 @@ describe "`create/2` with transferAllRights:" do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- has_end: DateTime.utc_now(),
+ has_end: Factory.now(),
}
- assert {:ok, _, tmp_res, _} = Domain.create(raise_params, %{name: Factory.str("name")})
+ assert {:ok, %EconomicEvent{} = evt} =
+ Domain.create(raise_params, %{name: Factory.str("name")})
+ evt = Domain.preload(evt, :resource_inventoried_as)
+ tmp_res = evt.resource_inventoried_as
# TODO: use combine-separate when implemented instead
Changeset.change(tmp_res, contained_in_id: params.to_resource_inventoried_as_id)
|> Repo.update()
assert {:error, "you can't transfer-all-rights into a container resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
test "fail when event's unit and to-resource's unit differ", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
|> Changeset.change(accounting_quantity_has_unit_id: Factory.insert!(:unit).id)
|> Repo.update!()
assert {:error, "the unit of resource-quantity must match with the unit of to-resource-inventoried-as"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
test "fail when resoure and to-resource don't conform to the same spec", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
|> Changeset.change(conforms_to_id: Factory.insert!(:resource_specification).id)
|> Repo.update!()
assert {:error, "the resources must conform to the same specification"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
end
@@ -1269,21 +1288,22 @@ describe "`create/2` with transfer:" do
has_unit_id: res.accounting_quantity_has_unit_id,
has_numerical_value: Factory.float(),
},
- has_point_in_time: DateTime.utc_now(),
+ has_point_in_time: Factory.now(),
}
- assert {:ok, _, to_res, _} = Domain.create(params, %{name: Factory.str("name")})
+ assert %EconomicEvent{resource_inventoried_as_id: to_res_id} =
+ Domain.create!(params, %{name: Factory.str("name")})
%{params: %{
action_id: "transfer",
provider_id: res.primary_accountable_id,
receiver_id: Factory.insert!(:agent).id,
resource_inventoried_as_id: res.id,
- to_resource_inventoried_as_id: to_res.id,
+ to_resource_inventoried_as_id: to_res_id,
resource_quantity: %{
has_unit_id: res.accounting_quantity_has_unit_id,
has_numerical_value: res.accounting_quantity_has_numerical_value,
},
- has_beginning: DateTime.utc_now(),
+ has_beginning: Factory.now(),
}}
else
%{params: %{
@@ -1295,13 +1315,13 @@ describe "`create/2` with transfer:" do
has_unit_id: res.accounting_quantity_has_unit_id,
has_numerical_value: res.accounting_quantity_has_numerical_value,
},
- has_beginning: DateTime.utc_now(),
+ has_beginning: Factory.now(),
}}
end
end
test "pass without `:to_resource_inventoried_as`", %{params: params} do
- {:ok, res_before} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ res_before = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
contained_ids = Enum.map(0..9, fn _ ->
agent = Factory.insert!(:agent)
@@ -1314,11 +1334,14 @@ describe "`create/2` with transfer:" do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- has_end: DateTime.utc_now(),
+ has_end: Factory.now(),
}
- assert {:ok, _, tmp_res, _} = Domain.create(raise_params, %{name: Factory.str("name")})
- assert {:ok, _} =
- Changeset.change(tmp_res, contained_in_id: params.resource_inventoried_as_id) |> Repo.update()
+ assert {:ok, %EconomicEvent{} = evt} =
+ Domain.create(raise_params, %{name: Factory.str("name")})
+ evt = Domain.preload(evt, :resource_inventoried_as)
+ tmp_res = evt.resource_inventoried_as
+ Changeset.change(tmp_res, contained_in_id: params.resource_inventoried_as_id)
+ |> Repo.update!()
tmp_res.id
end)
@@ -1336,8 +1359,13 @@ describe "`create/2` with transfer:" do
license: Factory.str("license"),
metadata: %{Factory.str("key") => Factory.str("val")},
}
- assert {:ok, %EconomicEvent{} = evt, _, %EconomicResource{} = to_res} = Domain.create(params, res_params)
- {:ok, res_after} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{} = evt}
+ = Domain.create(
+ params,
+ res_params)
+ evt = Domain.preload(evt, :to_resource_inventoried_as)
+ to_res = evt.to_resource_inventoried_as
+ res_after = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
assert res_after.accounting_quantity_has_numerical_value ==
res_before.accounting_quantity_has_numerical_value - params.resource_quantity.has_numerical_value
@@ -1376,12 +1404,12 @@ describe "`create/2` with transfer:" do
@tag :want_to_resource
test "pass with `:to_resource_inventoried_as`", %{params: params} do
- {:ok, res_before} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
- {:ok, to_res_before} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
+ res_before = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
+ to_res_before = EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
- assert {:ok, %EconomicEvent{}} = Domain.create(params, nil)
- {:ok, res_after} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
- {:ok, to_res_after} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{}} = Domain.create(params)
+ res_after = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
+ to_res_after = EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
assert res_after.accounting_quantity_has_numerical_value ==
res_before.accounting_quantity_has_numerical_value - params.resource_quantity.has_numerical_value
@@ -1395,100 +1423,96 @@ describe "`create/2` with transfer:" do
end
test "fail when provider doesn't have accountability over the resource", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.resource_inventoried_as_id)
|> Changeset.change(primary_accountable_id: Factory.insert!(:agent).id)
|> Repo.update!()
+
assert {:error, "you don't have accountability over this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when provider doesn't have custody over the resource", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.resource_inventoried_as_id)
|> Changeset.change(custodian_id: Factory.insert!(:agent).id)
|> Repo.update!()
+
assert {:error, "you don't have custody over this resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_contained
test "fail when the resource is a contained resource", %{params: params} do
assert {:error, "you can't transfer a contained resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when event's unit and resource's unit differ", %{params: params} do
params = update_in(params.resource_quantity.has_unit_id, fn _ -> Factory.insert!(:unit).id end)
assert {:error, "the unit of resource-quantity must match with the unit of resource-inventoried-as"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
test "fail when event's unit and to-resource's unit differ", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
|> Changeset.change(accounting_quantity_has_unit_id: Factory.insert!(:unit).id)
|> Repo.update!()
assert {:error, "the unit of resource-quantity must match with the unit of to-resource-inventoried-as"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_container
test "fail when the resource is a container and accounting-quantity is non-positive", %{params: params} do
err = "the transfer events need container resources to have positive accounting-quantity"
- {:ok, res} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ res = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
- Changeset.change(res, accounting_quantity_has_numerical_value: 0.0) |> Repo.update!()
- assert {:error, ^err} = Domain.create(params, nil)
+ Changeset.change(res, accounting_quantity_has_numerical_value: 0.0)
+ |> Repo.update!()
+ assert {:error, ^err} = Domain.create(params)
Changeset.change(res, accounting_quantity_has_numerical_value: -1.0) |> Repo.update!()
- assert {:error, ^err} = Domain.create(params, nil)
+ assert {:error, ^err} = Domain.create(params)
end
@tag :want_container
test "fail when the resource is a container and onhand-quantity is non-positive", %{params: params} do
err = "the transfer events need container resources to have positive onhand-quantity"
- {:ok, res} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ res = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
- Changeset.change(res, onhand_quantity_has_numerical_value: 0.0) |> Repo.update!()
- assert {:error, ^err} = Domain.create(params, nil)
+ Changeset.change(res, onhand_quantity_has_numerical_value: 0.0)
+ |> Repo.update!()
+ assert {:error, ^err} = Domain.create(params)
- Changeset.change(res, onhand_quantity_has_numerical_value: -1.0) |> Repo.update!()
- assert {:error, ^err} = Domain.create(params, nil)
+ Changeset.change(res, onhand_quantity_has_numerical_value: -1.0)
+ |> Repo.update!()
+ assert {:error, ^err} = Domain.create(params)
end
@tag :want_container
test "fail when event's quantity value and resource's accounting-quantity value differ", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.resource_inventoried_as_id)
|> Changeset.change(accounting_quantity_has_numerical_value: params.resource_quantity.has_numerical_value + 1)
|> Repo.update!()
+
assert {:error, "the transfer events need to fully transfer the resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_container
test "fail when event's quantity value and resource's onhnad-quantity value differ", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.resource_inventoried_as_id)
|> Changeset.change(onhand_quantity_has_numerical_value: params.resource_quantity.has_numerical_value + 1)
|> Repo.update!()
assert {:error, "the transfer events need to fully transfer the resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_container
@tag :want_to_resource
test "fail when transferring a container resource into another resource", %{params: params} do
assert {:error, "you can't transfer a container resource into another resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
@@ -1503,18 +1527,18 @@ describe "`create/2` with transfer:" do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- has_end: DateTime.utc_now(),
+ has_end: Factory.now(),
}
- assert {:ok, _, tmp_res, _} = Domain.create(raise_params, %{name: Factory.str("name")})
- # TODO: use combine-separate when implemented instead
- {:ok, res} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{resource_inventoried_as_id: tmp_res_id}} =
+ Domain.create(raise_params, %{name: Factory.str("name")})
- res
- |> Changeset.change(contained_in_id: tmp_res.id)
+ # TODO: use combine-separate when implemented instead
+ EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
+ |> Changeset.change(contained_in_id: tmp_res_id)
|> Repo.update!()
assert {:error, "you can't transfer into a contained resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
@@ -1529,27 +1553,28 @@ describe "`create/2` with transfer:" do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- has_end: DateTime.utc_now(),
+ has_end: Factory.now(),
}
- assert {:ok, _, tmp_res, _} = Domain.create(raise_params, %{name: Factory.str("name")})
+ assert {:ok, %EconomicEvent{} = evt} =
+ Domain.create(raise_params, %{name: Factory.str("name")})
+ evt = Domain.preload(evt, :resource_inventoried_as)
+ tmp_res = evt.resource_inventoried_as
# TODO: use combine-separate when implemented instead
Changeset.change(tmp_res, contained_in_id: params.to_resource_inventoried_as_id)
|> Repo.update()
assert {:error, "you can't transfer into a container resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
test "fail when resoure and to-resource don't conform to the same spec", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
|> Changeset.change(conforms_to_id: Factory.insert!(:resource_specification).id)
|> Repo.update!()
assert {:error, "the resources must conform to the same specification"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
end
@@ -1565,21 +1590,22 @@ describe "`create/2` with move:" do
has_unit_id: res.accounting_quantity_has_unit_id,
has_numerical_value: Factory.float(),
},
- has_point_in_time: DateTime.utc_now(),
+ has_point_in_time: Factory.now(),
}
- assert {:ok, _, to_res, _} = Domain.create(params, %{name: Factory.str("name")})
+ assert %EconomicEvent{resource_inventoried_as_id: to_res_id} =
+ Domain.create!(params, %{name: Factory.str("name")})
%{params: %{
action_id: "move",
provider_id: res.primary_accountable_id,
receiver_id: res.custodian_id,
resource_inventoried_as_id: res.id,
- to_resource_inventoried_as_id: to_res.id,
+ to_resource_inventoried_as_id: to_res_id,
resource_quantity: %{
has_unit_id: res.accounting_quantity_has_unit_id,
has_numerical_value: res.accounting_quantity_has_numerical_value,
},
- has_beginning: DateTime.utc_now(),
+ has_beginning: Factory.now(),
}}
else
%{params: %{
@@ -1591,13 +1617,13 @@ describe "`create/2` with move:" do
has_unit_id: res.accounting_quantity_has_unit_id,
has_numerical_value: res.accounting_quantity_has_numerical_value,
},
- has_beginning: DateTime.utc_now(),
+ has_beginning: Factory.now(),
}}
end
end
test "pass without `:to_resource_inventoried_as`", %{params: params} do
- {:ok, res_before} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ res_before = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
contained_ids = Enum.map(0..9, fn _ ->
agent = Factory.insert!(:agent)
@@ -1610,11 +1636,14 @@ describe "`create/2` with move:" do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- has_end: DateTime.utc_now(),
+ has_end: Factory.now(),
}
- assert {:ok, _, tmp_res, _} = Domain.create(raise_params, %{name: Factory.str("name")})
- assert {:ok, _} =
- Changeset.change(tmp_res, contained_in_id: params.resource_inventoried_as_id) |> Repo.update()
+ assert {:ok, %EconomicEvent{} = evt} =
+ Domain.create(raise_params, %{name: Factory.str("name")})
+ evt = Domain.preload(evt, :resource_inventoried_as)
+ tmp_res = evt.resource_inventoried_as
+ Changeset.change(tmp_res, contained_in_id: params.resource_inventoried_as_id)
+ |> Repo.update!()
tmp_res.id
end)
@@ -1632,8 +1661,11 @@ describe "`create/2` with move:" do
license: Factory.str("license"),
metadata: %{Factory.str("key") => Factory.str("val")},
}
- assert {:ok, %EconomicEvent{} = evt, _, %EconomicResource{} = to_res} = Domain.create(params, res_params)
- {:ok, res_after} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{} = evt} =
+ Domain.create(params, res_params)
+ evt = Domain.preload(evt, :to_resource_inventoried_as)
+ to_res = evt.to_resource_inventoried_as
+ res_after = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
assert res_after.accounting_quantity_has_numerical_value ==
res_before.accounting_quantity_has_numerical_value - params.resource_quantity.has_numerical_value
@@ -1672,12 +1704,12 @@ describe "`create/2` with move:" do
@tag :want_to_resource
test "pass with `:to_resource_inventoried_as`", %{params: params} do
- {:ok, res_before} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
- {:ok, to_res_before} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
+ res_before = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
+ to_res_before = EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
- assert {:ok, %EconomicEvent{}} = Domain.create(params, nil)
- {:ok, res_after} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
- {:ok, to_res_after} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{}} = Domain.create(params)
+ res_after = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
+ to_res_after = EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
assert res_after.accounting_quantity_has_numerical_value ==
res_before.accounting_quantity_has_numerical_value - params.resource_quantity.has_numerical_value
@@ -1691,122 +1723,114 @@ describe "`create/2` with move:" do
end
test "fail when provider doesn't have accountability over the resource", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.resource_inventoried_as_id)
|> Changeset.change(primary_accountable_id: Factory.insert!(:agent).id)
|> Repo.update!()
+
assert {:error, "you don't have accountability over resource-inventoried-as"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when provider doesn't have custody over the resource", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.resource_inventoried_as_id)
|> Changeset.change(custodian_id: Factory.insert!(:agent).id)
|> Repo.update!()
+
assert {:error, "you don't have custody over resource-inventoried-as"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
test "fail when provider doesn't have accountability over the to-resource", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
|> Changeset.change(primary_accountable_id: Factory.insert!(:agent).id)
|> Repo.update!()
+
assert {:error, "you don't have accountability over to-resource-inventoried-as"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
test "fail when provider doesn't have custody over the to-resource", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
|> Changeset.change(custodian_id: Factory.insert!(:agent).id)
|> Repo.update!()
+
assert {:error, "you don't have custody over to-resource-inventoried-as"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_contained
test "fail when the resource is a contained resource", %{params: params} do
assert {:error, "you can't move a contained resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
test "fail when event's unit and resource's unit differ", %{params: params} do
params = update_in(params.resource_quantity.has_unit_id, fn _ -> Factory.insert!(:unit).id end)
assert {:error, "the unit of resource-quantity must match with the unit of resource-inventoried-as"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
test "fail when event's unit and to-resource's unit differ", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
|> Changeset.change(accounting_quantity_has_unit_id: Factory.insert!(:unit).id)
|> Repo.update!()
assert {:error, "the unit of resource-quantity must match with the unit of to-resource-inventoried-as"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_container
test "fail when the resource is a container and accounting-quantity is non-positive", %{params: params} do
err = "the move events need container resources to have positive accounting-quantity"
- {:ok, res} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ res = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
Changeset.change(res, accounting_quantity_has_numerical_value: 0.0) |> Repo.update!()
- assert {:error, ^err} = Domain.create(params, nil)
+ assert {:error, ^err} = Domain.create(params)
Changeset.change(res, accounting_quantity_has_numerical_value: -1.0) |> Repo.update!()
- assert {:error, ^err} = Domain.create(params, nil)
+ assert {:error, ^err} = Domain.create(params)
end
@tag :want_container
test "fail when the resource is a container and onhand-quantity is non-positive", %{params: params} do
err = "the move events need container resources to have positive onhand-quantity"
- {:ok, res} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
+ res = EconomicResource.Domain.one!(params.resource_inventoried_as_id)
Changeset.change(res, onhand_quantity_has_numerical_value: 0.0) |> Repo.update!()
- assert {:error, ^err} = Domain.create(params, nil)
+ assert {:error, ^err} = Domain.create(params)
Changeset.change(res, onhand_quantity_has_numerical_value: -1.0) |> Repo.update!()
- assert {:error, ^err} = Domain.create(params, nil)
+ assert {:error, ^err} = Domain.create(params)
end
@tag :want_container
test "fail when event's quantity value and resource's accounting-quantity value differ", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.resource_inventoried_as_id)
|> Changeset.change(accounting_quantity_has_numerical_value: params.resource_quantity.has_numerical_value + 1)
|> Repo.update!()
+
assert {:error, "the move events need to fully move the resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_container
test "fail when event's quantity value and resource's onhnad-quantity value differ", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.resource_inventoried_as_id)
|> Changeset.change(onhand_quantity_has_numerical_value: params.resource_quantity.has_numerical_value + 1)
|> Repo.update!()
+
assert {:error, "the move events need to fully move the resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_container
@tag :want_to_resource
test "fail when transfering a container resource into another resource", %{params: params} do
assert {:error, "you can't move a container resource into another resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
@@ -1821,18 +1845,18 @@ describe "`create/2` with move:" do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- has_end: DateTime.utc_now(),
+ has_end: Factory.now(),
}
- assert {:ok, _, tmp_res, _} = Domain.create(raise_params, %{name: Factory.str("name")})
- # TODO: use combine-separate when implemented instead
- {:ok, res} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
+ assert {:ok, %EconomicEvent{resource_inventoried_as_id: tmp_res_id}} =
+ Domain.create(raise_params, %{name: Factory.str("name")})
- res
- |> Changeset.change(contained_in_id: tmp_res.id)
+ # TODO: use combine-separate when implemented instead
+ EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
+ |> Changeset.change(contained_in_id: tmp_res_id)
|> Repo.update!()
assert {:error, "you can't move into a contained resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
@@ -1847,27 +1871,28 @@ describe "`create/2` with move:" do
has_unit_id: Factory.insert!(:unit).id,
has_numerical_value: Factory.float(),
},
- has_end: DateTime.utc_now(),
+ has_end: Factory.now(),
}
- assert {:ok, _, tmp_res, _} = Domain.create(raise_params, %{name: Factory.str("name")})
+ assert {:ok, %EconomicEvent{} = evt} =
+ Domain.create(raise_params, %{name: Factory.str("name")})
+ evt = Domain.preload(evt, :resource_inventoried_as)
+ tmp_res = evt.resource_inventoried_as
# TODO: use combine-separate when implemented instead
Changeset.change(tmp_res, contained_in_id: params.to_resource_inventoried_as_id)
- |> Repo.update()
+ |> Repo.update!()
assert {:error, "you can't move into a container resource"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
@tag :want_to_resource
test "fail when resoure and to-resource don't conform to the same spec", %{params: params} do
- {:ok, res} = EconomicResource.Domain.one(params.to_resource_inventoried_as_id)
-
- res
+ EconomicResource.Domain.one!(params.to_resource_inventoried_as_id)
|> Changeset.change(conforms_to_id: Factory.insert!(:resource_specification).id)
|> Repo.update!()
assert {:error, "the resources must conform to the same specification"} =
- Domain.create(params, nil)
+ Domain.create(params)
end
end
diff --git a/test/vf/economic_resource/domain.test.exs b/test/vf/economic_resource/domain.test.exs
@@ -78,8 +78,7 @@ end
test "classifications/0 returns list of unique `classified_as` values" do
Enum.each(1..10, fn _ -> Factory.insert!(:economic_resource) end)
- {:ok, %{edges: edges}} = Domain.all()
- left = Enum.flat_map(edges, & &1.node.classified_as)
+ left = Enum.flat_map(Domain.all!(), & &1.classified_as)
right = Domain.classifications()
assert [] = left -- right
end
diff --git a/test/vf/event_or_commitment.test.exs b/test/vf/event_or_commitment.test.exs
@@ -33,7 +33,7 @@ describe "create EventOrCommitment" do
test "with both event and commitment", %{params: params} do
assert {:error, %Changeset{errors: errs}} =
params
- |> EventOrCommitment.chgset()
+ |> EventOrCommitment.changeset()
|> Repo.insert()
assert {:ok, _} = Keyword.fetch(errs, :event_id)
@@ -45,7 +45,7 @@ describe "create EventOrCommitment" do
assert {:ok, %EventOrCommitment{} = evt_comm} =
params
|> Map.delete(:commitment_id)
- |> EventOrCommitment.chgset()
+ |> EventOrCommitment.changeset()
|> Repo.insert()
assert evt_comm.event_id == params.event_id
@@ -57,7 +57,7 @@ describe "create EventOrCommitment" do
assert {:ok, %EventOrCommitment{} = evt_comm} =
params
|> Map.delete(:event_id)
- |> EventOrCommitment.chgset()
+ |> EventOrCommitment.changeset()
|> Repo.insert()
assert evt_comm.event_id == nil
@@ -71,7 +71,7 @@ describe "update EventOrCommitment" do
assert {:error, %Changeset{errors: errs}} =
:event_or_commitment
|> Factory.insert!(event: Factory.build(:economic_event), commitment: nil)
- |> EventOrCommitment.chgset(params)
+ |> EventOrCommitment.changeset(params)
|> Repo.update()
assert {:ok, _} = Keyword.fetch(errs, :commitment_id)
@@ -79,7 +79,7 @@ describe "update EventOrCommitment" do
assert {:error, %Changeset{errors: errs}} =
:event_or_commitment
|> Factory.insert!(event: nil, commitment: Factory.build(:commitment))
- |> EventOrCommitment.chgset(params)
+ |> EventOrCommitment.changeset(params)
|> Repo.update()
assert {:ok, _} = Keyword.fetch(errs, :event_id)
@@ -90,7 +90,7 @@ describe "update EventOrCommitment" do
assert {:ok, %EventOrCommitment{} = evt_comm} =
:event_or_commitment
|> Factory.insert!(event: Factory.build(:economic_event), commitment: nil)
- |> EventOrCommitment.chgset(
+ |> EventOrCommitment.changeset(
Map.delete(params, :commitment_id)
)
|> Repo.update()
@@ -104,7 +104,7 @@ describe "update EventOrCommitment" do
assert {:ok, %EventOrCommitment{} = evt_comm} =
:event_or_commitment
|> Factory.insert!(event: nil, commitment: Factory.build(:commitment))
- |> EventOrCommitment.chgset(
+ |> EventOrCommitment.changeset(
Map.delete(params, :event_id)
)
|> Repo.update()
diff --git a/test/vf/fulfillment.test.exs b/test/vf/fulfillment.test.exs
@@ -40,7 +40,7 @@ end
test "create Fulfillment", %{params: params} do
assert {:ok, %Fulfillment{} = fulf} =
params
- |> Fulfillment.chgset()
+ |> Fulfillment.changeset()
|> Repo.insert()
assert fulf.note == params.note
@@ -57,7 +57,7 @@ test "update Fulfillment", %{params: params} do
assert {:ok, %Fulfillment{} = fulf} =
:fulfillment
|> Factory.insert!()
- |> Fulfillment.chgset(params)
+ |> Fulfillment.changeset(params)
|> Repo.update()
assert fulf.note == params.note
diff --git a/test/vf/measure.test.exs b/test/vf/measure.test.exs
@@ -33,13 +33,13 @@ embedded_schema do
field :quantity_has_numerical_value, :float
end
-def chgset(params) do
+def changeset(params) do
%__MODULE__{}
|> common(params)
|> Map.put(:action, :insert)
end
-def chgset(schema, params) do
+def changeset(schema, params) do
schema
|> common(params)
|> Map.put(:action, :update)
@@ -67,50 +67,50 @@ end
test "insert", %{params: params} do
# no changes when params is `%{}`
- assert %Changeset{valid?: true, changes: %{}} = Dummy.chgset(%{})
+ assert %Changeset{valid?: true, changes: %{}} = Dummy.changeset(%{})
# fields are nil when `:quantity` is `nil`
- assert %Changeset{valid?: true, changes: chgs} = Dummy.chgset(%{quantity: nil})
+ assert %Changeset{valid?: true, changes: chgs} = Dummy.changeset(%{quantity: nil})
assert chgs.quantity_has_unit_id == nil
assert chgs.quantity_has_numerical_value == nil
# fields are properly set when `:quantity` is properly set
- assert %Changeset{valid?: true, changes: chgs} = Dummy.chgset(%{quantity: params})
+ assert %Changeset{valid?: true, changes: chgs} = Dummy.changeset(%{quantity: params})
assert chgs.quantity_has_unit_id == params.has_unit_id
assert chgs.quantity_has_numerical_value == params.has_numerical_value
# `:has_numerical_value` must be positive
assert %Changeset{valid?: false, errors: errs}
- = Dummy.chgset(%{quantity: Map.put(params, :has_numerical_value, 0)})
+ = Dummy.changeset(%{quantity: Map.put(params, :has_numerical_value, 0)})
assert length(Keyword.get_values(errs, :quantity)) == 1
assert %Changeset{valid?: false, errors: errs}
- = Dummy.chgset(%{quantity: Map.put(params, :has_numerical_value, -1)})
+ = Dummy.changeset(%{quantity: Map.put(params, :has_numerical_value, -1)})
assert length(Keyword.get_values(errs, :quantity)) == 1
# when no fields are provided, no fields are set
assert %Changeset{valid?: false, changes: chgs, errors: errs}
- = Dummy.chgset(%{quantity: %{}})
+ = Dummy.changeset(%{quantity: %{}})
assert length(Keyword.get_values(errs, :quantity)) == 2
refute Map.has_key?(chgs, :quantity_has_unit_id)
or Map.has_key?(chgs, :quantity_has_numerical_value)
# when `:has_unit_id` is `nil`, no fields are set
assert %Changeset{valid?: false, changes: chgs, errors: errs}
- = Dummy.chgset(%{quantity: %{has_unit_id: nil}})
+ = Dummy.changeset(%{quantity: %{has_unit_id: nil}})
assert length(Keyword.get_values(errs, :quantity)) == 2
refute Map.has_key?(chgs, :quantity_has_unit_id)
or Map.has_key?(chgs, :quantity_has_numerical_value)
# when `:has_numerical_value` is `nil`, no fields are set
assert %Changeset{valid?: false, changes: %{quantity: _}, errors: errs}
- = Dummy.chgset(%{quantity: %{has_numerical_value: nil}})
+ = Dummy.changeset(%{quantity: %{has_numerical_value: nil}})
assert length(Keyword.get_values(errs, :quantity)) == 2
refute Map.has_key?(chgs, :quantity_has_unit_id)
or Map.has_key?(chgs, :quantity_has_numerical_value)
# when both fields are `nil`, no fields are set
assert %Changeset{valid?: false, changes: %{quantity: _}, errors: errs}
- = Dummy.chgset(%{quantity: %{has_unit_id: nil, has_numerical_value: nil}})
+ = Dummy.changeset(%{quantity: %{has_unit_id: nil, has_numerical_value: nil}})
assert length(Keyword.get_values(errs, :quantity)) == 2
refute Map.has_key?(chgs, :quantity_has_unit_id)
or Map.has_key?(chgs, :quantity_has_numerical_value)
@@ -118,52 +118,52 @@ end
test "update", %{params: params, inserted: schema} do
# no changes when params is `%{}`
- assert %Changeset{valid?: true, changes: %{}} = Dummy.chgset(schema, %{})
+ assert %Changeset{valid?: true, changes: %{}} = Dummy.changeset(schema, %{})
# fields are nil when `:quantity` is `nil`
assert %Changeset{valid?: true, changes: %{
quantity_has_unit_id: nil,
quantity_has_numerical_value: nil,
- }} = Dummy.chgset(schema, %{quantity: nil})
+ }} = Dummy.changeset(schema, %{quantity: nil})
# fields are changed when `:quantity` is properly set
assert %Changeset{valid?: true, changes: chgs}
- = Dummy.chgset(schema, %{quantity: params})
+ = Dummy.changeset(schema, %{quantity: params})
assert chgs.quantity_has_unit_id == params.has_unit_id
assert chgs.quantity_has_numerical_value == params.has_numerical_value
# `:has_numerical_value` must be positive
assert %Changeset{valid?: false, errors: errs}
- = Dummy.chgset(schema, %{quantity: Map.put(params, :has_numerical_value, 0)})
+ = Dummy.changeset(schema, %{quantity: Map.put(params, :has_numerical_value, 0)})
assert length(Keyword.get_values(errs, :quantity)) == 1
assert %Changeset{valid?: false, errors: errs}
- = Dummy.chgset(schema, %{quantity: Map.put(params, :has_numerical_value, -1)})
+ = Dummy.changeset(schema, %{quantity: Map.put(params, :has_numerical_value, -1)})
assert length(Keyword.get_values(errs, :quantity)) == 1
# when no fields are provided, no fields are set
assert %Changeset{valid?: false, changes: chgs, errors: errs}
- = Dummy.chgset(schema, %{quantity: %{}})
+ = Dummy.changeset(schema, %{quantity: %{}})
assert length(Keyword.get_values(errs, :quantity)) == 2
refute Map.has_key?(chgs, :quantity_has_unit_id)
or Map.has_key?(chgs, :quantity_has_numerical_value)
# when `:has_unit_id` is `nil`, no fields are set
assert %Changeset{valid?: false, changes: chgs, errors: errs}
- = Dummy.chgset(schema, %{quantity: %{has_unit_id: nil}})
+ = Dummy.changeset(schema, %{quantity: %{has_unit_id: nil}})
assert length(Keyword.get_values(errs, :quantity)) == 2
refute Map.has_key?(chgs, :quantity_has_unit_id)
or Map.has_key?(chgs, :quantity_has_numerical_value)
# when `:has_numerical_value` is `nil`, no fields are set
assert %Changeset{valid?: false, changes: %{quantity: _}, errors: errs}
- = Dummy.chgset(schema, %{quantity: %{has_numerical_value: nil}})
+ = Dummy.changeset(schema, %{quantity: %{has_numerical_value: nil}})
assert length(Keyword.get_values(errs, :quantity)) == 2
refute Map.has_key?(chgs, :quantity_has_unit_id)
or Map.has_key?(chgs, :quantity_has_numerical_value)
# when both fields are `nil`, no fields are set
assert %Changeset{valid?: false, changes: %{quantity: _}, errors: errs}
- = Dummy.chgset(schema, %{quantity: %{has_unit_id: nil, has_numerical_value: nil}})
+ = Dummy.changeset(schema, %{quantity: %{has_unit_id: nil, has_numerical_value: nil}})
assert length(Keyword.get_values(errs, :quantity)) == 2
refute Map.has_key?(chgs, :quantity_has_unit_id)
or Map.has_key?(chgs, :quantity_has_numerical_value)
diff --git a/test/vf/proposal.test.exs b/test/vf/proposal.test.exs
@@ -34,7 +34,7 @@ end
test "create Proposal", %{params: params} do
assert {:ok, %Proposal{} = prop} =
params
- |> Proposal.chgset()
+ |> Proposal.changeset()
|> Repo.insert()
assert prop.name == params.name
@@ -49,7 +49,7 @@ test "update Proposal", %{params: params} do
assert {:ok, %Proposal{} = prop} =
:proposal
|> Factory.insert!()
- |> Proposal.chgset(params)
+ |> Proposal.changeset(params)
|> Repo.update()
assert prop.name == params.name
diff --git a/test/vf/proposed_intent.test.exs b/test/vf/proposed_intent.test.exs
@@ -31,7 +31,7 @@ end
test "create ProposedIntent", %{params: params} do
assert {:ok, %ProposedIntent{} = prop_int} =
params
- |> ProposedIntent.chgset()
+ |> ProposedIntent.changeset()
|> Repo.insert()
assert prop_int.reciprocal == params.reciprocal
@@ -43,7 +43,7 @@ test "update ProposedIntent", %{params: params} do
assert {:ok, %ProposedIntent{} = prop_int} =
:proposed_intent
|> Factory.insert!()
- |> ProposedIntent.chgset(params)
+ |> ProposedIntent.changeset(params)
|> Repo.update()
assert prop_int.reciprocal == params.reciprocal
diff --git a/test/vf/proposed_to.test.exs b/test/vf/proposed_to.test.exs
@@ -30,7 +30,7 @@ end
test "create ProposedTo", %{params: params} do
assert {:ok, %ProposedTo{} = prop_to} =
params
- |> ProposedTo.chgset()
+ |> ProposedTo.changeset()
|> Repo.insert()
assert prop_to.proposed_to_id == params.proposed_to_id
@@ -41,7 +41,7 @@ test "update ProposedTo", %{params: params} do
assert {:ok, %ProposedTo{} = prop_to} =
:proposed_to
|> Factory.insert!()
- |> ProposedTo.chgset(params)
+ |> ProposedTo.changeset(params)
|> Repo.update()
assert prop_to.proposed_to_id == params.proposed_to_id
diff --git a/test/vf/recipe_flow/domain.test.exs b/test/vf/recipe_flow/domain.test.exs
@@ -105,15 +105,17 @@ describe "create/1" do
assert new.effort_quantity_has_numerical_value == params.effort_quantity.has_numerical_value
end
- test "with bad params (without :resource_qunatity and :effort_quantit): doesn't create a RecipeFlow", %{params: params} do
+ test "with bad params (without :resource_qunatity and :effort_quantity): doesn't create a RecipeFlow", %{params: params} do
params =
params
|> Map.delete(:resource_quantity)
|> Map.delete(:effort_quantity)
assert {:error, %Changeset{errors: errs}} = Domain.create(params)
- assert Keyword.has_key?(errs, :resource_quantity)
- assert Keyword.has_key?(errs, :effort_quantity)
+ assert Keyword.has_key?(errs, :resource_quantity_has_numerical_value)
+ assert Keyword.has_key?(errs, :resource_quantity_has_unit_id)
+ assert Keyword.has_key?(errs, :effort_quantity_has_numerical_value)
+ assert Keyword.has_key?(errs, :effort_quantity_has_unit_id)
end
test "with bad params: doesn't create a RecipeFlow" do
diff --git a/test/vf/satisfaction.test.exs b/test/vf/satisfaction.test.exs
@@ -40,7 +40,7 @@ end
test "create Satisfaction", %{params: params} do
assert {:ok, %Satisfaction{} = satis} =
params
- |> Satisfaction.chgset()
+ |> Satisfaction.changeset()
|> Repo.insert()
assert satis.satisfied_by_id == params.satisfied_by_id
@@ -56,7 +56,7 @@ end
test "update Satisfaction", %{params: params} do
assert {:ok, %Satisfaction{} = satis} =
Factory.insert!(:satisfaction)
- |> Satisfaction.chgset(params)
+ |> Satisfaction.changeset(params)
|> Repo.update()
assert satis.satisfied_by_id == params.satisfied_by_id
diff --git a/test/vf/settlement.test.exs b/test/vf/settlement.test.exs
@@ -40,7 +40,7 @@ end
test "create Settlement", %{params: params} do
assert {:ok, %Settlement{} = settl} =
params
- |> Settlement.chgset()
+ |> Settlement.changeset()
|> Repo.insert()
assert settl.note == params.note
@@ -57,7 +57,7 @@ test "update Settlement", %{params: params} do
assert {:ok, %Settlement{} = settl} =
:settlement
|> Factory.insert!()
- |> Settlement.chgset(params)
+ |> Settlement.changeset(params)
|> Repo.update()
assert settl.note == params.note
diff --git a/test/vf/validate.test.exs b/test/vf/validate.test.exs
@@ -1,245 +0,0 @@
-# Zenflows is designed to implement the Valueflows vocabulary,
-# written and maintained by srfsh <info@dyne.org>.
-# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-defmodule ZenflowsTest.VF.Validate do
-use ExUnit.Case, async: true
-
-alias Ecto.Changeset
-alias Zenflows.VF.Validate
-
-@spec key_chgset(map()) :: Changeset.t()
-defp key_chgset(changes) do
- Changeset.change({%{}, %{key: :string}}, changes)
-end
-
-@spec name_chgset(map()) :: Changeset.t()
-defp name_chgset(changes) do
- Changeset.change({%{}, %{name: :string}}, changes)
-end
-
-@spec note_chgset(map()) :: Changeset.t()
-defp note_chgset(changes) do
- Changeset.change({%{}, %{note: :string}}, changes)
-end
-
-@spec img_chgset(map()) :: Changeset.t()
-defp img_chgset(changes) do
- Changeset.change({%{}, %{img: :string}}, changes)
-end
-
-@spec uri_chgset(map()) :: Changeset.t()
-defp uri_chgset(changes) do
- Changeset.change({%{}, %{uri: :string}}, changes)
-end
-
-@spec class_chgset(map()) :: Changeset.t()
-defp class_chgset(changes) do
- Changeset.change({%{}, %{list: {:array, :string}}}, changes)
-end
-
-describe "key/2" do
- test "with too short param" do
- assert %Changeset{errors: errs} =
- %{key: String.duplicate("a", 15)}
- |> key_chgset()
- |> Validate.key(:key)
-
- assert {:ok, _} = Keyword.fetch(errs, :key)
- end
-
- test "with too long param" do
- assert %Changeset{errors: errs} =
- %{key: String.duplicate("a", 2048 + 1)}
- |> key_chgset()
- |> Validate.key(:key)
-
- assert {:ok, _} = Keyword.fetch(errs, :key)
- end
-
- test "with the right size param" do
- assert %Changeset{errors: errs} =
- %{key: String.duplicate("a", 16)}
- |> key_chgset()
- |> Validate.key(:key)
-
- assert :error = Keyword.fetch(errs, :key)
- end
-end
-
-describe "name/2" do
- test "with too short param" do
- assert %Changeset{errors: errs} =
- %{name: ""}
- |> name_chgset()
- |> Validate.name(:name)
-
- assert {:ok, _} = Keyword.fetch(errs, :name)
- end
-
- test "with too long param" do
- assert %Changeset{errors: errs} =
- %{name: String.duplicate("a", 256 + 1)}
- |> name_chgset()
- |> Validate.name(:name)
-
- assert {:ok, _} = Keyword.fetch(errs, :name)
- end
-
- test "with the right size param" do
- assert %Changeset{errors: errs} =
- %{name: "aa"}
- |> name_chgset()
- |> Validate.name(:name)
-
- assert :error = Keyword.fetch(errs, :name)
- end
-end
-
-describe "note/2" do
- test "with too short param" do
- assert %Changeset{errors: errs} =
- %{note: ""}
- |> note_chgset()
- |> Validate.note(:note)
-
- assert {:ok, _} = Keyword.fetch(errs, :note)
- end
-
- test "with too long param" do
- assert %Changeset{errors: errs} =
- %{note: String.duplicate("a", 2048 + 1)}
- |> note_chgset()
- |> Validate.note(:note)
-
- assert {:ok, _} = Keyword.fetch(errs, :note)
- end
-
- test "with the right size param" do
- assert %Changeset{errors: errs} =
- %{note: "aa"}
- |> note_chgset()
- |> Validate.note(:note)
-
- assert :error = Keyword.fetch(errs, :note)
- end
-end
-
-describe "img/2" do
- test "with too short param" do
- assert %Changeset{errors: errs} =
- %{img: ""}
- |> img_chgset()
- |> Validate.img(:img)
-
- assert {:ok, _} = Keyword.fetch(errs, :img)
- end
-
- test "with too long param" do
- assert %Changeset{errors: errs} =
- %{img: String.duplicate("a", 25 * 1024 * 1024 + 1)}
- |> img_chgset()
- |> Validate.img(:img)
-
- assert {:ok, _} = Keyword.fetch(errs, :img)
- end
-
- test "with the right size param" do
- assert %Changeset{errors: errs} =
- %{img: String.duplicate("a", 1024)}
- |> img_chgset()
- |> Validate.img(:img)
-
- assert :error = Keyword.fetch(errs, :img)
- end
-end
-
-describe "uri/2" do
- test "with too short param" do
- assert %Changeset{errors: errs} =
- %{uri: ""}
- |> uri_chgset()
- |> Validate.uri(:uri)
-
- assert {:ok, _} = Keyword.fetch(errs, :uri)
- end
-
- test "with too long param" do
- assert %Changeset{errors: errs} =
- %{uri: String.duplicate("a", 512 + 1)}
- |> uri_chgset()
- |> Validate.uri(:uri)
-
- assert {:ok, _} = Keyword.fetch(errs, :uri)
- end
-
- test "with the right size param" do
- assert %Changeset{errors: errs} =
- %{uri: "https://example.test/example.jpg"}
- |> uri_chgset()
- |> Validate.uri(:uri)
-
- assert :error = Keyword.fetch(errs, :uri)
- end
-end
-
-describe "class/2" do
- test "with too few items" do
- assert %Changeset{errors: errs} =
- %{list: []}
- |> class_chgset()
- |> Validate.class(:list)
-
- assert {:ok, _} = Keyword.fetch(errs, :list)
- end
-
- test "with too many items" do
- assert %Changeset{errors: errs} =
- %{list: Enum.map(0..127 + 1, &("uri #{&1}"))}
- |> class_chgset()
- |> Validate.class(:list)
-
- assert {:ok, _} = Keyword.fetch(errs, :list)
- end
-
- test "with one of the items too short" do
- assert %Changeset{errors: errs} =
- %{list: Enum.map(0..64, &("uri #{&1}")) ++ [""]}
- |> class_chgset()
- |> Validate.class(:list)
-
- assert {:ok, _} = Keyword.fetch(errs, :list)
- end
-
- test "with one of the items too long" do
- assert %Changeset{errors: errs} =
- %{list: Enum.map(0..64, &("uri #{&1}")) ++ [String.duplicate("a", 513)]}
- |> class_chgset()
- |> Validate.class(:list)
-
- assert {:ok, _} = Keyword.fetch(errs, :list)
- end
-
- test "with everthing just right" do
- assert %Changeset{errors: errs} =
- %{list: ["aaa"]}
- |> class_chgset()
- |> Validate.class(:list)
-
- assert :error = Keyword.fetch(errs, :list)
- end
-end
-end