Commit 5e9d3da0 authored by Antonis Kalou's avatar Antonis Kalou

Add GraphQL for Claims

parent 5375b55b
# SPDX-License-Identifier: AGPL-3.0-only
defmodule ValueFlows.Claim.GraphQL do
# alias CommonsPub.Web.GraphQL.{CommonResolver}
require Logger
# use Absinthe.Schema.Notation
# import_sdl path: "lib/value_flows/graphql/schemas/claim.gql"
alias CommonsPub.{GraphQL, Repo}
alias CommonsPub.Meta.Pointers
alias CommonsPub.GraphQL.{FetchPage, ResolveField, ResolveRootPage}
alias ValueFlows.Claim.Claims
def claim(%{id: id}, info) do
ResolveField.run(%ResolveField{
module: __MODULE__,
fetcher: :fetch_claim,
context: id,
info: info
})
end
def claims(page_opts, info) do
ResolveRootPage.run(%ResolveRootPage{
module: __MODULE__,
fetcher: :fetch_claims,
page_opts: page_opts,
info: info,
cursor_validators: [&(is_integer(&1) and &1 >= 0), &Ecto.ULID.cast/1]
})
end
def fetch_claim(info, id) do
Claims.one([:default, id: id])
end
def fetch_events(page_opts, info) do
FetchPage.run(%FetchPage{
queries: ValueFlows.Claim.Queries,
query: ValueFlows.Claim,
page_opts: page_opts,
base_filters: [
:default,
user: GraphQL.current_user(info)
]
})
end
def fetch_resource_conforms_to_edge(%{resource_conforms_to_id: id} = thing, _, _)
when is_binary(id) do
thing = Repo.preload(thing, :resource_conforms_to)
{:ok, Map.get(thing, :resource_conforms_to)}
end
def fetch_resource_conforms_to_edge(_, _, _) do
{:ok, nil}
end
def fetch_triggered_by_edge(%{triggered_by_id: id} = thing, _, _) when is_binary(id) do
thing = Repo.preload(thing, :triggered_by)
{:ok, Map.get(thing, :triggered_by)}
end
def fetch_triggered_by_edge(_, _, _) do
{:ok, nil}
end
def create_claim(%{claim: %{provider: provider_id, receiver: receiver_id} = attrs}, info) do
with {:ok, user} <- GraphQL.current_user_or_not_logged_in(info),
{:ok, provider} <- Pointers.one(id: provider_id),
{:ok, receiver} <- Pointers.one(id: receiver_id),
{:ok, claim} <- Claims.create(user, provider, receiver, attrs) do
{:ok, %{claim: claim}}
end
end
def update_claim(%{claim: %{id: id} = attrs}, info) do
with {:ok, _user} <- GraphQL.current_user_or_not_logged_in(info),
{:ok, claim} <- claim(%{id: id}, info),
{:ok, claim} <- Claims.update(claim, attrs) do
{:ok, %{claim: claim}}
end
end
def delete_claim(%{id: id}, info) do
with {:ok, _user} <- GraphQL.current_user_or_not_logged_in(info),
{:ok, claim} <- claim(%{id: id}, info),
{:ok, _} <- Claims.soft_delete(claim) do
{:ok, true}
end
end
end
......@@ -10,7 +10,7 @@ defmodule ValueFlows.Claim.Migrations do
def up do
create_pointable_table(ValueFlows.Claim) do
add(:note, :text)
add(:agreed_in, :string)
add(:agreed_in, :text)
add(:action_id, :string)
add(:finished, :boolean)
......
......@@ -121,6 +121,35 @@ defmodule ValueFlows.Hydration do
resolve: &ValueFlows.Proposal.ProposedIntentGraphQL.published_in_edge/3
]
},
claim: %{
action: [
resolve: &ValueFlows.Knowledge.Action.GraphQL.action_edge/3,
],
provider: [
resolve: &ValueFlows.Util.GraphQL.fetch_provider_edge/3
],
receiver: [
resolve: &ValueFlows.Util.GraphQL.fetch_receiver_edge/3
],
resource_quantity: [
resolve: &ValueFlows.Util.GraphQL.resource_quantity_edge/3
],
effort_quantity: [
resolve: &ValueFlows.Util.GraphQL.effort_quantity_edge/3
],
resource_conforms_to: [
resolve: &ValueFlows.Claim.GraphQL.fetch_resource_conforms_to_edge/3,
],
triggered_by: [
resolve: &ValueFlows.Claim.GraphQL.fetch_triggered_by_edge/3,
],
in_scope_of: [
resolve: &CommonResolver.context_edge/3,
],
creator: [
resolve: &UsersResolver.creator_edge/3
],
},
economic_event: %{
canonical_url: [
resolve: &CommonsPub.Characters.GraphQL.Resolver.canonical_url_edge/3
......@@ -272,6 +301,14 @@ defmodule ValueFlows.Hydration do
resolve: &ValueFlows.Agent.GraphQL.organizations/2
],
# Claim
claim: [
resolve: &ValueFlows.Claim.GraphQL.claim/2,
],
claims: [
resolve: &ValueFlows.Claim.GraphQL.claims/2,
],
# Knowledge
action: [
resolve: &ValueFlows.Knowledge.Action.GraphQL.action/2
......@@ -366,6 +403,9 @@ defmodule ValueFlows.Hydration do
# start Mutation resolvers
value_flows_mutation: %{
create_claim: [
resolve: &ValueFlows.Claim.GraphQL.create_claim/2,
],
create_intent: [
resolve: &ValueFlows.Planning.Intent.GraphQL.create_intent/2
],
......@@ -387,6 +427,9 @@ defmodule ValueFlows.Hydration do
# create_action: [
# resolve: &ValueFlows.Knowledge.Action.GraphQL.create_action/2
# ],
update_claim: [
resolve: &ValueFlows.Claim.GraphQL.update_claim/2,
],
update_intent: [
resolve: &ValueFlows.Planning.Intent.GraphQL.update_intent/2
],
......@@ -402,6 +445,9 @@ defmodule ValueFlows.Hydration do
update_process: [
resolve: &ValueFlows.Observation.Process.GraphQL.update_process/2
],
delete_claim: [
resolve: &ValueFlows.Claim.GraphQL.delete_claim/2,
],
delete_intent: [
resolve: &ValueFlows.Planning.Intent.GraphQL.delete_intent/2
],
......
......@@ -32,6 +32,18 @@ defmodule ValueFlows.Simulate do
|> Map.put_new_lazy(:resource_classified_as, fn -> some(1..5, &url/0) end)
end
def claim_input(base \\ %{}) do
base
|> Map.put_new_lazy("note", &summary/0)
# FIXME: URI doesn't work, scalar?
# |> Map.put_new_lazy("agreedIn", &url/0)
|> Map.put_new_lazy("finished", &bool/0)
|> Map.put_new_lazy("created", &past_datetime_iso/0)
|> Map.put_new_lazy("due", &future_datetime_iso/0)
|> Map.put_new_lazy("action", &action_id/0)
# |> Map.put_new_lazy("resourceClassifiedAs", fn -> some(1..5, &url/0) end)
end
def agent_type(), do: Faker.Util.pick([:person, :organization])
def agent(base \\ %{}) do
......
......@@ -67,6 +67,24 @@ defmodule ValueFlows.Util.GraphQL do
{:ok, nil}
end
def resource_quantity_edge(%{resource_quantity_id: id} = thing, _, _) when not is_nil(id) do
thing = Repo.preload(thing, resource_quantity: [:unit])
{:ok, Map.get(thing, :resource_quantity)}
end
def resource_quantity_edge(_, _, _) do
{:ok, nil}
end
def effort_quantity_edge(%{effort_quantity_id: id} = thing, _, _) when not is_nil(id) do
thing = Repo.preload(thing, effort_quantity: [:unit])
{:ok, Map.get(thing, :effort_quantity)}
end
def effort_quantity_edge(_, _, _) do
{:ok, nil}
end
def accounting_quantity_edge(%{accounting_quantity_id: id} = thing, _, _) when not is_nil(id) do
thing = Repo.preload(thing, accounting_quantity: [:unit])
{:ok, Map.get(thing, :accounting_quantity)}
......
......@@ -247,6 +247,62 @@ defmodule ValueFlows.Test.Faking do
## Graphql
def claim_fields(extra \\ []) do
extra ++ ~w(id note agreed_in finished created due resource_classified_as)a
end
def claim_response_fields(extra \\ []) do
[claim: claim_fields(extra)]
end
def claim_query(options \\ []) do
options = Keyword.put_new(options, :id_type, :id)
gen_query(:id, &claim_subquery/1, options)
end
def claim_subquery(options \\ []) do
gen_subquery(:id, :claim, &claim_fields/1, options)
end
def claims_query(options \\ []) do
gen_query(&claims_subquery/1, options)
end
def claims_subquery(options \\ []) do
fields = Keyword.get(options, :fields, [])
fields = fields ++ claim_fields(fields)
field(:claims, [{:fields, fields} | options])
end
def create_claim_mutation(options \\ []) do
[claim: type!(:claim_create_params)]
|> gen_mutation(&create_claim_submutation/1, options)
end
def create_claim_submutation(options \\ []) do
[claim: var(:claim)]
|> gen_submutation(:create_claim, &claim_response_fields/1, options)
end
def update_claim_mutation(options \\ []) do
[claim: type!(:claim_update_params)]
|> gen_mutation(&update_claim_submutation/1, options)
end
def update_claim_submutation(options \\ []) do
[claim: var(:claim)]
|> gen_submutation(:update_claim, &claim_response_fields/1, options)
end
def delete_claim_mutation(options \\ []) do
[id: type!(:id)]
|> gen_mutation(&delete_claim_submutation/1, options)
end
def delete_claim_submutation(_options \\ []) do
field(:delete_claim, args: [id: var(:id)])
end
def person_fields(extra \\ []) do
extra ++
~w(name note agent_type canonical_url image display_username)a
......
# SPDX-License-Identifier: AGPL-3.0-only
defmodule ValueFlows.Claim.GraphQLTest do
use CommonsPub.Web.ConnCase, async: true
import CommonsPub.Test.Faking
import Measurement.Simulate
import ValueFlows.Simulate
import ValueFlows.Test.Faking
@schema CommonsPub.Web.GraphQL.Schema
describe "Claim" do
test "fetches a claim by ID (via HTTP)" do
user = fake_user!()
claim = fake_claim!(user)
q = claim_query()
conn = user_conn(user)
assert fetched = grumble_post_key(q, conn, :claim, %{id: claim.id})
assert_claim(fetched)
assert fetched["id"] == claim.id
end
test "fetched a full nested claim by ID (via Absinthe.run)" do
user = fake_user!()
unit = fake_unit!(user)
claim = fake_claim!(user, %{
in_scope_of: [fake_community!(user).id],
resource_quantity: measure(%{unit_id: unit.id}),
effort_quantity: measure(%{unit_id: unit.id}),
resource_conforms_to: fake_resource_specification!(user).id,
triggered_by: fake_economic_event!(user).id,
})
assert queried =
CommonsPub.Web.GraphQL.QueryHelper.run_query_id(
claim.id,
@schema,
:claim,
3
)
assert_claim(queried)
end
end
describe "createClaim" do
test "creates a new claim" do
user = fake_user!()
q = create_claim_mutation()
conn = user_conn(user)
vars = %{claim: claim_input(%{
"provider" => fake_user!().id,
"receiver" => fake_user!().id,
})}
assert claim = grumble_post_key(q, conn, :create_claim, vars)["claim"]
assert_claim(claim)
end
test "fails for a guest user" do
q = create_claim_mutation()
vars = %{claim: claim_input(%{
"provider" => fake_user!().id,
"receiver" => fake_user!().id,
})}
assert [%{"code" => "needs_login"}] = grumble_post_errors(q, json_conn(), vars)
end
end
describe "updateClaim" do
test "updates an existing claim" do
user = fake_user!()
claim = fake_claim!(user)
q = update_claim_mutation()
conn = user_conn(user)
vars = %{claim: claim_input(%{"id" => claim.id})}
assert updated = grumble_post_key(q, conn, :update_claim, vars)["claim"]
assert_claim(updated)
assert updated["id"] == claim.id
end
test "fails for a guest user" do
claim = fake_claim!(fake_user!())
q = update_claim_mutation()
vars = %{claim: claim_input(%{"id" => claim.id})}
assert [%{"code" => "needs_login"}] = grumble_post_errors(q, json_conn(), vars)
end
end
describe "deleteClaim" do
test "deletes an existing claim" do
user = fake_user!()
claim = fake_claim!(user)
q = delete_claim_mutation()
conn = user_conn(user)
vars = %{id: claim.id}
assert grumble_post_key(q, conn, :delete_claim, vars)
end
test "fails for a guest user" do
claim = fake_claim!(fake_user!())
q = delete_claim_mutation()
vars = %{id: claim.id}
assert [%{"code" => "needs_login"}] = grumble_post_errors(q, json_conn(), vars)
end
end
end
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment