From 7917b19b1d510c37286616b9711c9bd7cf99b432 Mon Sep 17 00:00:00 2001 From: Luca Zaninotto Date: Wed, 8 Jan 2025 16:39:31 +0100 Subject: [PATCH] chore(pagination): paginate device relationships Part of #779 Paginates `:ota_operations` and `:tags` relationships with relay Signed-off-by: Luca Zaninotto --- backend/lib/edgehog/devices/device/device.ex | 5 ++ .../schema/mutation/add_device_tags_test.exs | 29 ++++++++---- .../mutation/remove_device_tags_test.exs | 28 +++++++---- .../schema/mutation/update_device_test.exs | 7 +++ .../edgehog_web/schema/query/device_test.exs | 46 ++++++++++++++----- 5 files changed, 86 insertions(+), 29 deletions(-) diff --git a/backend/lib/edgehog/devices/device/device.ex b/backend/lib/edgehog/devices/device/device.ex index 79427cdbe..5f4b44629 100644 --- a/backend/lib/edgehog/devices/device/device.ex +++ b/backend/lib/edgehog/devices/device/device.ex @@ -50,6 +50,11 @@ defmodule Edgehog.Devices.Device do graphql do type :device + + # TODO: add :device_groups as a relay-paginated relationship. Since it's a + # manual relationship, it needs to implement callbacks that define + # datalayer subqueries so Ash can compose and support the functionality. + paginate_relationship_with ota_operations: :relay, tags: :relay end actions do diff --git a/backend/test/edgehog_web/schema/mutation/add_device_tags_test.exs b/backend/test/edgehog_web/schema/mutation/add_device_tags_test.exs index 314be19c1..fb791e332 100644 --- a/backend/test/edgehog_web/schema/mutation/add_device_tags_test.exs +++ b/backend/test/edgehog_web/schema/mutation/add_device_tags_test.exs @@ -35,7 +35,7 @@ defmodule EdgehogWeb.Schema.Mutation.AddDeviceTagsTest do test "successfully adds tags", %{tenant: tenant, id: id} do result = add_device_tags_mutation(tenant: tenant, id: id, tags: ["foo", "bar"]) - assert %{"tags" => tags} = extract_result!(result) + assert tags = extract_result_tags!(result) assert length(tags) == 2 tag_names = Enum.map(tags, &Map.fetch!(&1, "name")) assert "foo" in tag_names @@ -46,7 +46,7 @@ defmodule EdgehogWeb.Schema.Mutation.AddDeviceTagsTest do result = add_device_tags_mutation(tenant: tenant, id: id, tags: ["FOO", "bAr", " BaZ ", "baz"]) - assert %{"tags" => tags} = extract_result!(result) + assert tags = extract_result_tags!(result) assert length(tags) == 3 tag_names = Enum.map(tags, &Map.fetch!(&1, "name")) assert "foo" in tag_names @@ -62,7 +62,7 @@ defmodule EdgehogWeb.Schema.Mutation.AddDeviceTagsTest do result = add_device_tags_mutation(tenant: tenant, id: id, tags: ["foo", "baz"]) - assert %{"tags" => tags} = extract_result!(result) + assert tags = extract_result_tags!(result) assert length(tags) == 3 tag_names = Enum.map(tags, &Map.fetch!(&1, "name")) assert "foo" in tag_names @@ -92,8 +92,13 @@ defmodule EdgehogWeb.Schema.Mutation.AddDeviceTagsTest do mutation AddDeviceTags($id: ID!, $input: AddDeviceTagsInput!) { addDeviceTags(id: $id, input: $input) { result { - tags { - name + tags(first: 10, sort: [{field: NAME, order: ASC}]) { + count + edges { + node { + name + } + } } } } @@ -122,21 +127,27 @@ defmodule EdgehogWeb.Schema.Mutation.AddDeviceTagsTest do error end - defp extract_result!(result) do + defp extract_result_tags!(result) do refute :errors in Map.keys(result) refute "errors" in Map.keys(result[:data]) assert %{ data: %{ "addDeviceTags" => %{ - "result" => device + "result" => %{ + "tags" => %{ + "count" => count, + "edges" => edges + } + } } } } = result - assert device != nil + tags = Enum.map(edges, & &1["node"]) + assert length(tags) == count - device + tags end defp non_existing_device_id(tenant) do diff --git a/backend/test/edgehog_web/schema/mutation/remove_device_tags_test.exs b/backend/test/edgehog_web/schema/mutation/remove_device_tags_test.exs index 95298a279..e6237ae71 100644 --- a/backend/test/edgehog_web/schema/mutation/remove_device_tags_test.exs +++ b/backend/test/edgehog_web/schema/mutation/remove_device_tags_test.exs @@ -39,13 +39,13 @@ defmodule EdgehogWeb.Schema.Mutation.RemoveDeviceTagsTest do test "successfully removes tags", %{tenant: tenant, id: id} do result = remove_device_tags_mutation(tenant: tenant, id: id, tags: ["foo"]) - assert %{"tags" => [%{"name" => "bar"}]} = extract_result!(result) + assert [%{"name" => "bar"}] = extract_result!(result) end test "normalizes tag names", %{tenant: tenant, id: id} do result = remove_device_tags_mutation(tenant: tenant, id: id, tags: ["FOO", " bar "]) - assert %{"tags" => []} = extract_result!(result) + assert [] = extract_result!(result) end test "is idempotent and works with non-existing tags", ctx do @@ -58,7 +58,7 @@ defmodule EdgehogWeb.Schema.Mutation.RemoveDeviceTagsTest do result = remove_device_tags_mutation(tenant: tenant, id: id, tags: ["bar", "baz"]) - assert %{"tags" => [%{"name" => "foo"}]} = extract_result!(result) + assert [%{"name" => "foo"}] = extract_result!(result) end test "fails with empty tags", %{tenant: tenant, id: id} do @@ -83,8 +83,13 @@ defmodule EdgehogWeb.Schema.Mutation.RemoveDeviceTagsTest do mutation RemoveDeviceTags($id: ID!, $input: RemoveDeviceTagsInput!) { removeDeviceTags(id: $id, input: $input) { result { - tags { - name + tags(first: 10, sort: [{ field: NAME, order: ASC }]) { + count + edges { + node { + name + } + } } } } @@ -120,14 +125,21 @@ defmodule EdgehogWeb.Schema.Mutation.RemoveDeviceTagsTest do assert %{ data: %{ "removeDeviceTags" => %{ - "result" => device + "result" => %{ + "tags" => %{ + "count" => count, + "edges" => edges + } + } } } } = result - assert device != nil + tags = Enum.map(edges, & &1["node"]) - device + assert length(tags) == count + + tags end defp non_existing_device_id(tenant) do diff --git a/backend/test/edgehog_web/schema/mutation/update_device_test.exs b/backend/test/edgehog_web/schema/mutation/update_device_test.exs index 9eb850e7e..179a5c1e1 100644 --- a/backend/test/edgehog_web/schema/mutation/update_device_test.exs +++ b/backend/test/edgehog_web/schema/mutation/update_device_test.exs @@ -52,6 +52,13 @@ defmodule EdgehogWeb.Schema.Mutation.UpdateDeviceTest do name deviceId online + otaOperations { + edges { + node { + id + } + } + } } } } diff --git a/backend/test/edgehog_web/schema/query/device_test.exs b/backend/test/edgehog_web/schema/query/device_test.exs index 8e3f83333..f9913c9c7 100644 --- a/backend/test/edgehog_web/schema/query/device_test.exs +++ b/backend/test/edgehog_web/schema/query/device_test.exs @@ -94,20 +94,44 @@ defmodule EdgehogWeb.Schema.Query.DeviceTest do query ($id: ID!) { device(id: $id) { otaOperations { - id - baseImageUrl + count + edges { + cursor + node { + id + baseImageUrl + } + } + pageInfo { + endCursor + hasNextPage + } } } } """ - %{"otaOperations" => [ota_operation]} = - [document: document, tenant: tenant, id: id] - |> device_query() - |> extract_result!() - - assert ota_operation["id"] == ota_operation_id - assert ota_operation["baseImageUrl"] == base_image_url + assert %{ + "otaOperations" => %{ + "count" => 1, + "edges" => [ + %{ + "cursor" => cursor, + "node" => %{ + "id" => ^ota_operation_id, + "baseImageUrl" => ^base_image_url + } + } + ], + "pageInfo" => %{ + "endCursor" => cursor, + "hasNextPage" => false + } + } + } = + [document: document, tenant: tenant, id: id] + |> device_query() + |> extract_result!() end test "returns nil if non existing", %{tenant: tenant} do @@ -574,8 +598,6 @@ defmodule EdgehogWeb.Schema.Query.DeviceTest do setup %{tenant: tenant} do fixture = device_fixture(tenant: tenant) - device_id = fixture.device_id - id = AshGraphql.Resource.encode_relay_id(fixture) document = """ @@ -588,7 +610,7 @@ defmodule EdgehogWeb.Schema.Query.DeviceTest do } """ - %{device: fixture, device_id: device_id, tenant: tenant, id: id, document: document} + %{device: fixture, tenant: tenant, id: id, document: document} end test "is empty with no groups", ctx do