zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

commit b1b5bbdd73b3f46e6826ce34005024208ce63f21
parent a25a4b94b528d7a389d9fcce60052bcdb25b17be
Author: srfsh <dev@srf.sh>
Date:   Tue, 23 Aug 2022 08:14:31 +0300

Zenflows{Test,}.VF.Organization: add paging support and small improvements

Diffstat:
Msrc/zenflows/vf/organization.ex | 1+
Msrc/zenflows/vf/organization/domain.ex | 55++++++++++++++++++++++++++-----------------------------
Msrc/zenflows/vf/organization/resolv.ex | 20++++++++++++--------
Msrc/zenflows/vf/organization/type.ex | 39++++++++++++++++++++++++++++-----------
Mtest/vf/organization/domain.test.exs | 79++++++++++++++++++++++++++++++++++---------------------------------------------
Mtest/vf/organization/type.test.exs | 68+++++++++++++++++++++++++++++++-------------------------------------
6 files changed, 132 insertions(+), 130 deletions(-)

diff --git a/src/zenflows/vf/organization.ex b/src/zenflows/vf/organization.ex @@ -29,6 +29,7 @@ alias Zenflows.VF.{SpatialThing, Validate} note: String.t() | nil, primary_location: SpatialThing.t() | nil, classified_as: [String.t()] | nil, + updated_at: DateTime.t(), } schema "vf_agent" do diff --git a/src/zenflows/vf/organization/domain.ex b/src/zenflows/vf/organization/domain.ex @@ -22,43 +22,51 @@ import Ecto.Query alias Ecto.Multi alias Zenflows.DB.Repo +alias Zenflows.GQL.Paging alias Zenflows.VF.Organization @typep repo() :: Ecto.Repo.t() @typep chgset() :: Ecto.Changeset.t() -@typep changes() :: Ecto.Multi.changes() @typep id() :: Zenflows.DB.Schema.id() @typep params() :: Zenflows.DB.Schema.params() -@spec by_id(repo(), id()) :: Organization.t() | nil -def by_id(repo \\ Repo, id) do - repo.get_by(Organization, id: id, type: :org) +@spec one(repo(), 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 + nil -> {:error, "not found"} + found -> {:ok, found} + end end -@spec all() :: [Organization.t()] -def all() do - Organization - |> from() - |> where(type: :org) - |> Repo.all() +@spec all(Paging.params()) :: Paging.result(Organization.t()) +def all(params) do + Paging.page(where(Organization, type: :org), params) end @spec create(params()) :: {:ok, Organization.t()} | {:error, chgset()} def create(params) do Multi.new() - |> Multi.insert(:org, Organization.chgset(params)) + |> Multi.insert(:insert, Organization.chgset(params)) |> Repo.transaction() |> case do - {:ok, %{org: o}} -> {:ok, o} + {:ok, %{insert: o}} -> {:ok, o} {:error, _, cset, _} -> {:error, cset} end end -@spec update(id(), params()) :: {:ok, Organization.t()} | {:error, String.t() | chgset()} +@spec update(id(), params()) + :: {:ok, Organization.t()} | {:error, String.t() | chgset()} def update(id, params) do Multi.new() - |> Multi.run(:get, multi_get(id)) - |> Multi.update(:update, &Organization.chgset(&1.get, params)) + |> Multi.put(:id, id) + |> Multi.run(:one, &one/2) + |> Multi.update(:update, &Organization.chgset(&1.one, params)) |> Repo.transaction() |> case do {:ok, %{update: o}} -> {:ok, o} @@ -69,8 +77,9 @@ end @spec delete(id()) :: {:ok, Organization.t()} | {:error, String.t() | chgset()} def delete(id) do Multi.new() - |> Multi.run(:get, multi_get(id)) - |> Multi.delete(:delete, &(&1.get)) + |> Multi.put(:id, id) + |> Multi.run(:one, &one/2) + |> Multi.delete(:delete, &(&1.one)) |> Repo.transaction() |> case do {:ok, %{delete: o}} -> {:ok, o} @@ -82,16 +91,4 @@ end def preload(org, :primary_location) do Repo.preload(org, :primary_location) end - -# Returns an Organization in ok-err tuple from given ID. Used inside -# Ecto.Multi.run/5 to get a record in transaction. -@spec multi_get(id()) :: (repo(), changes() -> {:ok, Organization.t()} | {:error, String.t()}) -defp multi_get(id) do - fn repo, _ -> - case by_id(repo, id) do - nil -> {:error, "not found"} - org -> {:ok, org} - end - end -end end diff --git a/src/zenflows/vf/organization/resolv.ex b/src/zenflows/vf/organization/resolv.ex @@ -20,36 +20,40 @@ defmodule Zenflows.VF.Organization.Resolv do alias Zenflows.VF.{Agent, Organization, Organization.Domain} -def organization(%{id: id}, _info) do - {:ok, Domain.by_id(id)} +def organization(params, _) do + Domain.one(params) end -def create_organization(%{organization: params}, _info) do +def organizations(params, _) do + Domain.all(params) +end + +def create_organization(%{organization: params}, _) do with {:ok, org} <- Domain.create(params) do {:ok, %{agent: org}} end end -def update_organization(%{organization: %{id: id} = params}, _info) do +def update_organization(%{organization: %{id: id} = params}, _) do with {:ok, org} <- Domain.update(id, params) do {:ok, %{agent: org}} end end -def delete_organization(%{id: id}, _info) do +def delete_organization(%{id: id}, _) do with {:ok, _} <- Domain.delete(id) do {:ok, true} end end -def primary_location(%Organization{} = org, _args, _info) do +def primary_location(%Organization{} = org, _, _) do org = Domain.preload(org, :primary_location) {:ok, org.primary_location} end # For some reason, Absinthe calls this one instead of the one on # Zenflows.VF.Agent.Type for queries to Agent itself. -def primary_location(%Agent{} = agent, args, info) do - Agent.Resolv.primary_location(agent, args, info) +def primary_location(%Agent{} = agent, params, info) do + Agent.Resolv.primary_location(agent, params, info) end end diff --git a/src/zenflows/vf/organization/type.ex b/src/zenflows/vf/organization/type.ex @@ -33,6 +33,7 @@ occur and mail can be sent. This is usually a mappable geographic location. It also could be a website address, as in the case of agents who have no physical location. """ +@primary_location_id "(`SpatialThing`) #{@primary_location}" @note "A textual description or comment." @classified_as """ References one or more concepts in a common taxonomy or other @@ -62,10 +63,6 @@ object :organization do field :classified_as, list_of(non_null(:string)) end -object :organization_response do - field :agent, non_null(:organization) -end - input_object :organization_create_params do @desc @name field :name, non_null(:string) @@ -76,10 +73,7 @@ input_object :organization_create_params do @desc @note field :note, :string - # TODO: When - # https://github.com/absinthe-graphql/absinthe/issues/1126 results, - # apply the correct changes if any. - @desc "(`SpatialThing`) " <> @primary_location + @desc @primary_location_id field :primary_location_id, :id, name: "primary_location" @desc @classified_as @@ -98,13 +92,27 @@ input_object :organization_update_params do @desc @note field :note, :string - @desc "(`SpatialThing`) " <> @primary_location + @desc @primary_location_id field :primary_location_id, :id, name: "primary_location" @desc @classified_as field :classified_as, list_of(non_null(:string)) end +object :organization_response do + field :agent, non_null(:organization) +end + +object :organization_edge do + field :cursor, non_null(:id) + field :node, non_null(:organization) +end + +object :organization_connection do + field :page_info, non_null(:page_info) + field :edges, non_null(list_of(non_null(:organization_edge))) +end + object :query_organization do @desc "Find an organization (group) agent by its ID." field :organization, :organization do @@ -112,8 +120,17 @@ object :query_organization do resolve &Resolv.organization/2 end - #"Loads all organizations publicly registered within this collaboration space" - #organizations(start: ID, limit: Int): [Organization!] + @desc """ + Loads all organizations publicly registered within this + collaboration space. + """ + field :organizations, :organization_connection do + arg :first, :integer + arg :after, :id + arg :last, :integer + arg :before, :id + resolve &Resolv.organizations/2 + end end object :mutation_organization do diff --git a/test/vf/organization/domain.test.exs b/test/vf/organization/domain.test.exs @@ -21,68 +21,52 @@ use ZenflowsTest.Help.EctoCase, async: true alias Ecto.Changeset alias Zenflows.VF.{Organization, Organization.Domain} -setup ctx do - if ctx[:no_insert] do - :ok - else - params = %{ +setup do + %{ + params: %{ name: Factory.uniq("name"), image: Factory.img(), classified_as: Factory.uniq_list("uri"), note: Factory.uniq("note"), primary_location_id: Factory.insert!(:spatial_thing).id, - } - - %{params: params, org: Factory.insert!(:organization)} - end + }, + inserted: Factory.insert!(:organization), + } end -describe "by_id/1" do - test "returns an Organization", %{org: org} do - assert %Organization{type: :org} = Domain.by_id(org.id) +describe "one/1" do + test "with good id: finds the Organization", %{inserted: %{id: id}} do + assert {:ok, %Organization{}} = Domain.one(id) end - test "doesn't return a Person" do + test "with per's id: doesn't return a Person" do per = Factory.insert!(:person) - - assert Domain.by_id(per.id) == nil + assert {:error, "not found"} = Domain.one(per.id) end -end - -@tag :no_insert -test "all/0 returns all Organizations" do - want_ids = - Enum.map(1..10, fn _ -> Factory.insert!(:organization).id end) - |> Enum.sort() - have_ids = - Domain.all() - |> Enum.map(& &1.id) - |> Enum.sort() - assert have_ids == want_ids + test "with bad id: doesn't find the Organization" do + assert {:error, "not found"} = Domain.one(Factory.id()) + end end describe "create/1" do - test "creates an Organization with valid params", %{params: params} do - assert {:ok, %Organization{} = org} = Domain.create(params) - - assert org.type == :org - assert org.name == params.name - assert org.image == params.image - assert org.classified_as == params.classified_as - assert org.note == params.note - assert org.primary_location_id == params.primary_location_id - end + test "with good params: creates an Organization", %{params: params} do + assert {:ok, %Organization{} = new} = Domain.create(params) + assert new.type == :org + assert new.name == params.name + assert new.image == params.image + assert new.classified_as == params.classified_as + assert new.note == params.note + assert new.primary_location_id == params.primary_location_id end - test "doesn't create an Organization with invalid params" do + test "with bad params: doesn't create an Organization" do assert {:error, %Changeset{}} = Domain.create(%{}) end end describe "update/2" do - test "updates an Organization with valid params", %{params: params, org: old} do + test "with good params: updates the Organization", %{params: params, inserted: old} do assert {:ok, %Organization{} = new} = Domain.update(old.id, params) - assert new.name == params.name assert new.classified_as == params.classified_as assert new.note == params.note @@ -90,9 +74,8 @@ describe "update/2" do assert new.primary_location_id == params.primary_location_id end - test "doesn't update an Organization with invalid params", %{org: old} do + test "with bad params: doesn't update the Organization", %{inserted: old} do assert {:ok, %Organization{} = new} = Domain.update(old.id, %{}) - assert new.name == old.name assert new.classified_as == old.classified_as assert new.note == old.note @@ -101,8 +84,14 @@ describe "update/2" do end end -test "delete/1 deletes an Organization", %{org: %{id: id}} do - assert {:ok, %Organization{id: ^id}} = Domain.delete(id) - assert Domain.by_id(id) == nil +describe "delete/1" do + test "with good id: deletes the Organization", %{inserted: %{id: id}} do + assert {:ok, %Organization{id: ^id}} = Domain.delete(id) + assert {:error, "not found"} = Domain.one(id) + end + + test "with bad id: doesn't delete the Organization" do + assert {:error, "not found"} = Domain.delete(Factory.id()) + end end end diff --git a/test/vf/organization/type.test.exs b/test/vf/organization/type.test.exs @@ -27,32 +27,37 @@ setup do "note" => Factory.uniq("note"), "primaryLocation" => Factory.insert!(:spatial_thing).id, }, - org: Factory.insert!(:organization), + inserted: Factory.insert!(:organization), } end +@frag """ +fragment organization on Organization { + id + name + note + image + primaryLocation { id } + classifiedAs +} +""" + describe "Query" do - test "organization()", %{org: org} do + test "organization()", %{inserted: new} do assert %{data: %{"organization" => data}} = run!(""" + #{@frag} query ($id: ID!) { - organization(id: $id) { - id - name - note - image - primaryLocation { id } - classifiedAs - } + organization(id: $id) {...organization} } - """, vars: %{"id" => org.id}) + """, vars: %{"id" => new.id}) - assert data["id"] == org.id - assert data["name"] == org.name - assert data["note"] == org.note - assert data["image"] == org.image - assert data["primaryLocation"]["id"] == org.primary_location_id - assert data["classifiedAs"] == org.classified_as + assert data["id"] == new.id + assert data["name"] == new.name + assert data["note"] == new.note + assert data["image"] == new.image + assert data["primaryLocation"]["id"] == new.primary_location_id + assert data["classifiedAs"] == new.classified_as end end @@ -60,16 +65,10 @@ describe "Mutation" do test "createOrganization", %{params: params} do assert %{data: %{"createOrganization" => %{"agent" => data}}} = run!(""" + #{@frag} mutation ($organization: OrganizationCreateParams!) { createOrganization(organization: $organization) { - agent { - id - name - note - image - primaryLocation { id } - classifiedAs - } + agent {...organization} } } """, vars: %{"organization" => params}) @@ -84,39 +83,34 @@ describe "Mutation" do assert data == params end - test "updateOrganization()", %{params: params, org: org} do + test "updateOrganization()", %{params: params, inserted: old} do assert %{data: %{"updateOrganization" => %{"agent" => data}}} = run!(""" + #{@frag} mutation ($organization: OrganizationUpdateParams!) { updateOrganization(organization: $organization) { - agent { - id - name - note - image - primaryLocation { id } - classifiedAs - } + agent {...organization} } } """, vars: %{"organization" => params |> Map.take(~w[name note image primaryLocation classifiedAs]) - |> Map.put("id", org.id) + |> Map.put("id", old.id) }) + assert data["id"] == old.id keys = ~w[name image note classifiedAs] assert Map.take(data, keys) == Map.take(params, keys) assert data["primaryLocation"]["id"] == params["primaryLocation"] end - test "deleteOrganization", %{org: org} do + test "deleteOrganization", %{inserted: %{id: id}} do assert %{data: %{"deleteOrganization" => true}} = run!(""" mutation ($id: ID!) { deleteOrganization(id: $id) } - """, vars: %{"id" => org.id}) + """, vars: %{"id" => id}) end end end